Merge pull request #1779 from blockstack/feat/clarity-strings

Introduce string-ascii and string-utf8 (Clarity types)
This commit is contained in:
Ludo Galabru
2020-08-24 15:00:54 -04:00
committed by GitHub
27 changed files with 1520 additions and 547 deletions

View File

@@ -485,7 +485,7 @@ X := the size of the list _entry_ type
### concat
The cost of concatting two lists or buffers is linear in
the size of the two iterables:
the size of the two sequences:
```
a + b * (X+Y)

View File

@@ -222,9 +222,9 @@ checking pass.
Some functions require additional work from the static analysis system.
## Functions on iterables (e.g., map, filter, fold)
## Functions on sequences (e.g., map, filter, fold)
Functions on iterables need to perform an additional check that the
Functions on sequences need to perform an additional check that the
supplied type is a list or buffer before performing the normal
argument type checking. This cost is assessed as:
@@ -236,7 +236,7 @@ where a is a constant.
## Functions on options/responses
Similarly to the functions on iterables, option/response functions
Similarly to the functions on sequences, option/response functions
must perform a simple check to see if the supplied input is an option or
response before performing additional argument type checking. This cost is
assessed as:

View File

@@ -83,6 +83,8 @@ pub enum ContractInterfaceAtomType {
bool,
principal,
buffer { length: u32 },
string_utf8 { length: u32 },
string_ascii { length: u32 },
tuple(Vec<ContractInterfaceTupleEntryType>),
optional(Box<ContractInterfaceAtomType>),
response { ok: Box<ContractInterfaceAtomType>, error: Box<ContractInterfaceAtomType> },
@@ -125,6 +127,8 @@ impl ContractInterfaceAtomType {
pub fn from_type_signature(sig: &TypeSignature) -> ContractInterfaceAtomType {
use vm::types::TypeSignature::*;
use vm::types::{SequenceSubtype::*, StringSubtype::*};
match sig {
NoType => ContractInterfaceAtomType::none,
IntType => ContractInterfaceAtomType::int128,
@@ -132,9 +136,11 @@ impl ContractInterfaceAtomType {
BoolType => ContractInterfaceAtomType::bool,
PrincipalType => ContractInterfaceAtomType::principal,
TraitReferenceType(_) => ContractInterfaceAtomType::trait_reference,
BufferType(len) => ContractInterfaceAtomType::buffer { length: len.into() },
TupleType(sig) => Self::from_tuple_type(sig),
ListType(list_data) => {
TupleType(sig) => ContractInterfaceAtomType::from_tuple_type(sig),
SequenceType(StringType(ASCII(len))) => ContractInterfaceAtomType::string_ascii { length: len.into() },
SequenceType(StringType(UTF8(len))) => ContractInterfaceAtomType::string_utf8 { length: len.into() },
SequenceType(BufferType(len)) => ContractInterfaceAtomType::buffer { length: len.into() },
SequenceType(ListType(list_data)) => {
let (type_f, length) = list_data.clone().destruct();
ContractInterfaceAtomType::list {
type_f: Box::new(Self::from_type_signature(&type_f)), length }

View File

@@ -15,6 +15,7 @@ pub enum CheckErrors {
MemoryBalanceExceeded(u64, u64),
ValueTooLarge,
ValueOutOfBounds,
TypeSignatureTooDeep,
ExpectedName,
@@ -111,7 +112,7 @@ pub enum CheckErrors {
// expect a function, or applying a function to a list
NonFunctionApplication,
ExpectedListApplication,
ExpectedListOrBuffer(TypeSignature),
ExpectedSequence(TypeSignature),
MaxLengthOverflow,
// let syntax
@@ -147,6 +148,9 @@ pub enum CheckErrors {
TraitBasedContractCallInReadOnly,
ContractOfExpectsTrait,
// strings
InvalidCharactersDetected,
WriteAttemptedInReadOnly,
AtBlockClosureMustBeReadOnly
}
@@ -287,6 +291,7 @@ impl DiagnosableError for CheckErrors {
CheckErrors::BadSyntaxExpectedListOfPairs => "bad syntax: function expects a list of pairs to bind names, e.g., ((name-0 a) (name-1 b) ...)".into(),
CheckErrors::UnknownTypeName(name) => format!("failed to parse type: '{}'", name),
CheckErrors::ValueTooLarge => format!("created a type which was greater than maximum allowed value size"),
CheckErrors::ValueOutOfBounds => format!("created a type which value size was out of defined bounds"),
CheckErrors::TypeSignatureTooDeep => "created a type which was deeper than maximum allowed type depth".into(),
CheckErrors::ExpectedName => format!("expected a name argument to this function"),
CheckErrors::NoSuperType(a, b) => format!("unable to create a supertype for the two types: '{}' and '{}'", a, b),
@@ -335,7 +340,7 @@ impl DiagnosableError for CheckErrors {
CheckErrors::NameAlreadyUsed(name) => format!("defining '{}' conflicts with previous value", name),
CheckErrors::NonFunctionApplication => format!("expecting expression of type function"),
CheckErrors::ExpectedListApplication => format!("expecting expression of type list"),
CheckErrors::ExpectedListOrBuffer(found_type) => format!("expecting expression of type 'list' or 'buff', found '{}'", found_type),
CheckErrors::ExpectedSequence(found_type) => format!("expecting expression of type 'list', 'buff', 'string-ascii' or 'string-utf8' - found '{}'", found_type),
CheckErrors::MaxLengthOverflow => format!("expecting a value <= {}", u32::max_value()),
CheckErrors::BadLetSyntax => format!("invalid syntax of 'let'"),
CheckErrors::CircularReference(function_names) => format!("detected interdependent functions ({})", function_names.join(", ")),
@@ -368,6 +373,7 @@ impl DiagnosableError for CheckErrors {
CheckErrors::DefineTraitBadSignature => format!("invalid trait definition"),
CheckErrors::TraitReferenceNotAllowed => format!("trait references can not be stored"),
CheckErrors::ContractOfExpectsTrait => format!("trait reference expected"),
CheckErrors::InvalidCharactersDetected => format!("invalid characters detected"),
CheckErrors::TypeAlreadyAnnotatedFailure | CheckErrors::CheckerImplementationFailure => {
format!("internal error - please file an issue on github.com/blockstack/blockstack-core")
},

View File

@@ -208,7 +208,7 @@ fn test_non_function_application() {
fn test_expected_list_or_buff() {
let snippet = "(filter not 4)";
let err = mem_type_check(snippet).unwrap_err();
assert!(format!("{}", err.diagnostic).contains("expecting expression of type 'list' or 'buff'"));
assert!(format!("{}", err.diagnostic).contains("expecting expression of type 'list', 'buff', 'string-ascii' or 'string-utf8'"));
}
#[test]

View File

@@ -12,7 +12,7 @@ use std::convert::TryFrom;
use vm::costs::{cost_functions, analysis_typecheck_cost, CostOverflowingMath};
mod assets;
mod iterables;
mod sequences;
mod maps;
mod options;
@@ -415,13 +415,13 @@ impl TypedNativeFunction {
Let => Special(SpecialNativeFunction(&check_special_let)),
FetchVar => Special(SpecialNativeFunction(&check_special_fetch_var)),
SetVar => Special(SpecialNativeFunction(&check_special_set_var)),
Map => Special(SpecialNativeFunction(&iterables::check_special_map)),
Filter => Special(SpecialNativeFunction(&iterables::check_special_filter)),
Fold => Special(SpecialNativeFunction(&iterables::check_special_fold)),
Append => Special(SpecialNativeFunction(&iterables::check_special_append)),
Concat => Special(SpecialNativeFunction(&iterables::check_special_concat)),
AsMaxLen => Special(SpecialNativeFunction(&iterables::check_special_as_max_len)),
Len => Special(SpecialNativeFunction(&iterables::check_special_len)),
Map => Special(SpecialNativeFunction(&sequences::check_special_map)),
Filter => Special(SpecialNativeFunction(&sequences::check_special_filter)),
Fold => Special(SpecialNativeFunction(&sequences::check_special_fold)),
Append => Special(SpecialNativeFunction(&sequences::check_special_append)),
Concat => Special(SpecialNativeFunction(&sequences::check_special_concat)),
AsMaxLen => Special(SpecialNativeFunction(&sequences::check_special_as_max_len)),
Len => Special(SpecialNativeFunction(&sequences::check_special_len)),
ListCons => Special(SpecialNativeFunction(&check_special_list_cons)),
FetchEntry => Special(SpecialNativeFunction(&maps::check_special_fetch_entry)),
SetEntry => Special(SpecialNativeFunction(&maps::check_special_set_entry)),

View File

@@ -1,8 +1,9 @@
use vm::functions::NativeFunctions;
use vm::representations::{SymbolicExpression, SymbolicExpressionType};
use vm::types::{ TypeSignature, FunctionType };
use vm::types::{TypeSignature, FunctionType};
use vm::types::{SequenceSubtype::*, StringSubtype::*};
use vm::types::{Value, MAX_VALUE_SIZE};
pub use vm::types::signatures::{ListTypeData, BufferLength};
pub use vm::types::signatures::{ListTypeData, BufferLength, StringUTF8Length};
use std::convert::TryFrom;
use std::convert::TryInto;
@@ -38,20 +39,28 @@ pub fn check_special_map(checker: &mut TypeChecker, args: &[SymbolicExpression],
runtime_cost!(cost_functions::ANALYSIS_ITERABLE_FUNC, checker, 1)?;
let argument_type = checker.type_check(&args[1], context)?;
match argument_type {
TypeSignature::ListType(list_data) => {
let (mapped_type, len) = match argument_type {
TypeSignature::SequenceType(ListType(list_data)) => {
let (arg_items_type, arg_length) = list_data.destruct();
let mapped_type = function_type.check_args(checker, &[arg_items_type])?;
TypeSignature::list_of(mapped_type, arg_length)
.map_err(|_| CheckErrors::ConstructedListTooLarge.into())
(mapped_type, arg_length)
},
TypeSignature::BufferType(buffer_data) => {
TypeSignature::SequenceType(BufferType(buffer_data)) => {
let mapped_type = function_type.check_args(checker, &[TypeSignature::min_buffer()])?;
TypeSignature::list_of(mapped_type, buffer_data.into())
.map_err(|_| CheckErrors::ConstructedListTooLarge.into())
(mapped_type, buffer_data.into())
},
_ => Err(CheckErrors::ExpectedListOrBuffer(argument_type).into())
}
TypeSignature::SequenceType(StringType(ASCII(ascii_data))) => {
let mapped_type = function_type.check_args(checker, &[TypeSignature::min_string_ascii()])?;
(mapped_type, ascii_data.into())
},
TypeSignature::SequenceType(StringType(UTF8(utf8_data))) => {
let mapped_type = function_type.check_args(checker, &[TypeSignature::min_string_utf8()])?;
(mapped_type, utf8_data.into())
},
_ => return Err(CheckErrors::ExpectedSequence(argument_type).into())
};
TypeSignature::list_of(mapped_type, len)
.map_err(|_| CheckErrors::ConstructedListTooLarge.into())
}
pub fn check_special_filter(checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext) -> TypeResult {
@@ -68,9 +77,8 @@ pub fn check_special_filter(checker: &mut TypeChecker, args: &[SymbolicExpressio
{
let input_type = match argument_type {
TypeSignature::ListType(ref list_data) => Ok(list_data.clone().destruct().0),
TypeSignature::BufferType(_) => Ok(TypeSignature::min_buffer()),
_ => Err(CheckErrors::ExpectedListOrBuffer(argument_type.clone()))
TypeSignature::SequenceType(ref sequence_type) => Ok(sequence_type.unit_type()),
_ => Err(CheckErrors::ExpectedSequence(argument_type.clone()))
}?;
let filter_type = function_type.check_args(checker, &[input_type])?;
@@ -96,9 +104,8 @@ pub fn check_special_fold(checker: &mut TypeChecker, args: &[SymbolicExpression]
let argument_type = checker.type_check(&args[1], context)?;
let input_type = match argument_type {
TypeSignature::ListType(list_data) => Ok(list_data.destruct().0),
TypeSignature::BufferType(_) => Ok(TypeSignature::min_buffer()),
_ => Err(CheckErrors::ExpectedListOrBuffer(argument_type))
TypeSignature::SequenceType(sequence_type) => Ok(sequence_type.unit_type()),
_ => Err(CheckErrors::ExpectedSequence(argument_type))
}?;
let initial_value_type = checker.type_check(&args[2], context)?;
@@ -126,33 +133,40 @@ pub fn check_special_concat(checker: &mut TypeChecker, args: &[SymbolicExpressio
analysis_typecheck_cost(checker, &lhs_type, &rhs_type)?;
match lhs_type {
TypeSignature::ListType(lhs_list) => {
if let TypeSignature::ListType(rhs_list) = rhs_type {
let (lhs_entry_type, lhs_max_len) = lhs_list.destruct();
let (rhs_entry_type, rhs_max_len) = rhs_list.destruct();
let list_entry_type = TypeSignature::least_supertype(&lhs_entry_type, &rhs_entry_type)?;
let new_len = lhs_max_len.checked_add(rhs_max_len)
.ok_or(CheckErrors::MaxLengthOverflow)?;
let return_type = TypeSignature::list_of(list_entry_type, new_len)?;
return Ok(return_type);
} else {
return Err(CheckErrors::TypeError(rhs_type.clone(), TypeSignature::ListType(lhs_list)).into());
let res = match (&lhs_type, &rhs_type) {
(TypeSignature::SequenceType(lhs_seq), TypeSignature::SequenceType(rhs_seq)) => {
match (lhs_seq, rhs_seq) {
(ListType(lhs_list), ListType(rhs_list)) => {
let (lhs_entry_type, lhs_max_len) = (lhs_list.get_list_item_type(), lhs_list.get_max_len());
let (rhs_entry_type, rhs_max_len) = (rhs_list.get_list_item_type(), rhs_list.get_max_len());
let list_entry_type = TypeSignature::least_supertype(lhs_entry_type, rhs_entry_type)?;
let new_len = lhs_max_len.checked_add(rhs_max_len)
.ok_or(CheckErrors::MaxLengthOverflow)?;
let type_sig = TypeSignature::list_of(list_entry_type, new_len)?;
type_sig
},
(BufferType(lhs_len), BufferType(rhs_len)) => {
let size: u32 = u32::from(lhs_len).checked_add(u32::from(rhs_len))
.ok_or(CheckErrors::MaxLengthOverflow)?;
TypeSignature::SequenceType(BufferType(size.try_into()?))
},
(StringType(ASCII(lhs_len)), StringType(ASCII(rhs_len))) => {
let size: u32 = u32::from(lhs_len).checked_add(u32::from(rhs_len))
.ok_or(CheckErrors::MaxLengthOverflow)?;
TypeSignature::SequenceType(StringType(ASCII(size.try_into()?)))
},
(StringType(UTF8(lhs_len)), StringType(UTF8(rhs_len))) => {
let size: u32 = u32::from(lhs_len).checked_add(u32::from(rhs_len))
.ok_or(CheckErrors::MaxLengthOverflow)?;
TypeSignature::SequenceType(StringType(UTF8(size.try_into()?)))
},
(_, _) => return Err(CheckErrors::TypeError(lhs_type.clone(), rhs_type.clone()).into())
}
},
TypeSignature::BufferType(lhs_buff_len) => {
if let TypeSignature::BufferType(rhs_buff_len) = rhs_type {
let size: u32 = u32::from(lhs_buff_len).checked_add(u32::from(rhs_buff_len))
.ok_or(CheckErrors::MaxLengthOverflow)?;
let return_type = TypeSignature::BufferType(size.try_into()?);
return Ok(return_type);
} else {
return Err(CheckErrors::TypeError(rhs_type.clone(), TypeSignature::max_buffer()).into());
}
},
_ => Err(CheckErrors::ExpectedListOrBuffer(lhs_type.clone()).into())
}
}
_ => return Err(CheckErrors::ExpectedSequence(lhs_type.clone()).into())
};
Ok(res)
}
pub fn check_special_append(checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext) -> TypeResult {
@@ -162,7 +176,7 @@ pub fn check_special_append(checker: &mut TypeChecker, args: &[SymbolicExpressio
let lhs_type = checker.type_check(&args[0], context)?;
match lhs_type {
TypeSignature::ListType(lhs_list) => {
TypeSignature::SequenceType(ListType(lhs_list)) => {
let rhs_type = checker.type_check(&args[1], context)?;
let (lhs_entry_type, lhs_max_len) = lhs_list.destruct();
@@ -194,19 +208,25 @@ pub fn check_special_as_max_len(checker: &mut TypeChecker, args: &[SymbolicExpre
let expected_len = u32::try_from(expected_len)
.map_err(|_e| CheckErrors::MaxLengthOverflow)?;
let iterable = checker.type_check(&args[0], context)?;
analysis_typecheck_cost(checker, &iterable, &iterable)?;
let sequence = checker.type_check(&args[0], context)?;
analysis_typecheck_cost(checker, &sequence, &sequence)?;
match iterable {
TypeSignature::ListType(list) => {
match sequence {
TypeSignature::SequenceType(ListType(list)) => {
let (lhs_entry_type, _) = list.destruct();
let resized_list = ListTypeData::new_list(lhs_entry_type, expected_len)?;
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::ListType(resized_list))))
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::SequenceType(ListType(resized_list)))))
},
TypeSignature::BufferType(_) => {
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::BufferType(BufferLength::try_from(expected_len).unwrap()))))
TypeSignature::SequenceType(BufferType(_)) => {
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::SequenceType(BufferType(BufferLength::try_from(expected_len).unwrap())))))
},
_ => Err(CheckErrors::ExpectedListOrBuffer(iterable).into())
TypeSignature::SequenceType(StringType(ASCII(_))) => {
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::SequenceType(StringType(ASCII(BufferLength::try_from(expected_len).unwrap()))))))
},
TypeSignature::SequenceType(StringType(UTF8(_))) => {
Ok(TypeSignature::OptionalType(Box::new(TypeSignature::SequenceType(StringType(UTF8(StringUTF8Length::try_from(expected_len).unwrap()))))))
},
_ => Err(CheckErrors::ExpectedSequence(sequence).into())
}
}
@@ -217,8 +237,8 @@ pub fn check_special_len(checker: &mut TypeChecker, args: &[SymbolicExpression],
runtime_cost!(cost_functions::ANALYSIS_ITERABLE_FUNC, checker, 1)?;
match collection_type {
TypeSignature::ListType(_) | TypeSignature::BufferType(_) => Ok(()),
_ => Err(CheckErrors::ExpectedListOrBuffer(collection_type.clone()))
TypeSignature::SequenceType(_) => Ok(()),
_ => Err(CheckErrors::ExpectedSequence(collection_type.clone()))
}?;
Ok(TypeSignature::UIntType)

View File

@@ -1,16 +1,16 @@
use vm::database::MemoryBackingStore;
use vm::types::{TypeSignature, QualifiedContractIdentifier};
use vm::types::{TypeSignature, SequenceSubtype, StringSubtype, QualifiedContractIdentifier};
use vm::ast::parse;
use vm::analysis::errors::CheckErrors;
use vm::analysis::{AnalysisDatabase, mem_type_check};
use std::convert::TryInto;
fn buff_type(size: u32) -> TypeSignature {
TypeSignature::BufferType(size.try_into().unwrap()).into()
fn string_ascii_type(size: u32) -> TypeSignature {
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(size.try_into().unwrap()))).into()
}
const FIRST_CLASS_TOKENS: &str = "(define-fungible-token stackaroos)
(define-non-fungible-token stacka-nfts (buff 10))
(define-non-fungible-token stacka-nfts (string-ascii 10))
(nft-get-owner? stacka-nfts \"1234567890\" )
(define-read-only (my-ft-get-balance (account principal))
(ft-get-balance stackaroos account))
@@ -142,16 +142,16 @@ fn test_bad_asset_usage() {
TypeSignature::IntType),
CheckErrors::BadTokenName,
CheckErrors::NoSuchNFT("stackoos".to_string()),
CheckErrors::TypeError(buff_type(10),
CheckErrors::TypeError(string_ascii_type(10),
TypeSignature::UIntType),
CheckErrors::TypeError(buff_type(10),
buff_type(15)),
CheckErrors::TypeError(string_ascii_type(10),
string_ascii_type(15)),
CheckErrors::BadTokenName,
CheckErrors::NoSuchNFT("stackoos".to_string()),
CheckErrors::TypeError(buff_type(10),
CheckErrors::TypeError(string_ascii_type(10),
TypeSignature::UIntType),
CheckErrors::TypeError(buff_type(10),
buff_type(15)),
CheckErrors::TypeError(string_ascii_type(10),
string_ascii_type(15)),
CheckErrors::TypeError(TypeSignature::PrincipalType,
TypeSignature::UIntType),
CheckErrors::NoSuchFT("stackoos".to_string()),
@@ -166,7 +166,7 @@ fn test_bad_asset_usage() {
TypeSignature::UIntType),
CheckErrors::TypeError(TypeSignature::PrincipalType,
TypeSignature::UIntType),
CheckErrors::TypeError(buff_type(10),
CheckErrors::TypeError(string_ascii_type(10),
TypeSignature::UIntType),
CheckErrors::NoSuchFT("stackoos".to_string()),
CheckErrors::BadTokenName,

View File

@@ -12,7 +12,9 @@ use vm::types::{Value, PrincipalData, TypeSignature, FunctionType, FixedFunction
QualifiedContractIdentifier};
use vm::database::MemoryBackingStore;
use vm::types::TypeSignature::{IntType, BoolType, BufferType, UIntType, PrincipalType};
use vm::types::TypeSignature::{IntType, BoolType, SequenceType, UIntType, PrincipalType};
use vm::types::{SequenceSubtype::*, StringSubtype::*};
use std::convert::TryInto;
mod assets;
@@ -23,7 +25,11 @@ fn type_check_helper(exp: &str) -> TypeResult {
}
fn buff_type(size: u32) -> TypeSignature {
TypeSignature::BufferType(size.try_into().unwrap()).into()
TypeSignature::SequenceType(BufferType(size.try_into().unwrap())).into()
}
fn ascii_type(size: u32) -> TypeSignature {
TypeSignature::SequenceType(StringType(ASCII(size.try_into().unwrap()))).into()
}
#[test]
@@ -410,7 +416,7 @@ fn test_simple_ifs() {
"(if true true false)",
"(if true \"abcdef\" \"abc\")",
"(if true \"a\" \"abcdef\")" ];
let expected = [ "int", "bool", "(buff 6)", "(buff 6)" ];
let expected = [ "int", "bool", "(string-ascii 6)", "(string-ascii 6)" ];
let bad = ["(if true true 1)",
"(if true \"a\" false)",
@@ -419,7 +425,7 @@ fn test_simple_ifs() {
let bad_expected = [
CheckErrors::IfArmsMustMatch(BoolType, IntType),
CheckErrors::IfArmsMustMatch(buff_type(1), BoolType),
CheckErrors::IfArmsMustMatch(ascii_type(1), BoolType),
CheckErrors::IncorrectArgumentCount(3, 0),
CheckErrors::TypeError(BoolType, IntType)
];
@@ -563,8 +569,8 @@ fn test_lists() {
CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()),
CheckErrors::IncorrectArgumentCount(2, 1),
CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType),
CheckErrors::ExpectedListOrBuffer(UIntType),
CheckErrors::ExpectedListOrBuffer(IntType)];
CheckErrors::ExpectedSequence(UIntType),
CheckErrors::ExpectedSequence(IntType)];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&good_test).unwrap()));
@@ -583,8 +589,8 @@ fn test_buff() {
"(if true \"block\" \"blockstack\")",
"(len \"blockstack\")"];
let expected = [
"(buff 10)",
"(buff 10)",
"(string-ascii 10)",
"(string-ascii 10)",
"uint"];
let bad = [
"(fold and (list true false) 2)",
@@ -614,8 +620,8 @@ fn test_buff() {
CheckErrors::IllegalOrUnknownFunctionApplication("if".to_string()),
CheckErrors::IncorrectArgumentCount(2, 1),
CheckErrors::UnionTypeError(vec![IntType, UIntType], BoolType),
CheckErrors::ExpectedListOrBuffer(UIntType),
CheckErrors::ExpectedListOrBuffer(IntType)];
CheckErrors::ExpectedSequence(UIntType),
CheckErrors::ExpectedSequence(IntType)];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&good_test).unwrap()));
@@ -630,13 +636,13 @@ fn test_buff() {
fn test_buff_fold() {
let good = [
"(define-private (get-len (x (buff 1)) (acc uint)) (+ acc u1))
(fold get-len \"101010\" u0)",
(fold get-len 0x000102030405 u0)",
"(define-private (slice (x (buff 1)) (acc (tuple (limit uint) (cursor uint) (data (buff 10)))))
(if (< (get cursor acc) (get limit acc))
(let ((data (default-to (get data acc) (as-max-len? (concat (get data acc) x) u10))))
(tuple (limit (get limit acc)) (cursor (+ u1 (get cursor acc))) (data data)))
acc))
(fold slice \"0123456789\" (tuple (limit u5) (cursor u0) (data \"\")))"];
(fold slice 0x00010203040506070809 (tuple (limit u5) (cursor u0) (data 0x)))"];
let expected = ["uint", "(tuple (cursor uint) (data (buff 10)) (limit uint))"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
@@ -648,7 +654,7 @@ fn test_buff_fold() {
#[test]
fn test_buff_map() {
let good = [
"(map hash160 \"12345\")"];
"(map hash160 0x0102030405)"];
let expected = ["(list 5 (buff 20))"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
@@ -674,9 +680,9 @@ fn test_buff_as_max_len() {
"(as-max-len? \"12345\" u8)",
"(as-max-len? \"12345\" u4)"];
let expected = [
"(optional (buff 5))",
"(optional (buff 8))",
"(optional (buff 4))"];
"(optional (string-ascii 5))",
"(optional (string-ascii 8))",
"(optional (string-ascii 4))"];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&test).unwrap()));
@@ -756,7 +762,7 @@ fn test_concat_append_supertypes() {
#[test]
fn test_buff_concat() {
let good = [
"(concat \"123\" \"58\")"];
"(concat 0x010203 0x0405)"];
let expected = ["(buff 5)"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
@@ -767,9 +773,9 @@ fn test_buff_concat() {
#[test]
fn test_buff_filter() {
let good = [
"(define-private (f (e (buff 1))) (is-eq e \"1\"))
"(define-private (f (e (string-ascii 1))) (is-eq e \"1\"))
(filter f \"101010\")"];
let expected = ["(buff 6)"];
let expected = ["(string-ascii 6)"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
let type_sig = mem_type_check(good_test).unwrap().0.unwrap();
@@ -1162,10 +1168,10 @@ fn test_set_list_variable() {
#[test]
fn test_set_buffer_variable() {
let contract_src = r#"
(define-data-var name (buff 5) "alice")
(define-data-var name (string-ascii 5) "alice")
(define-private (get-name)
(var-get name))
(define-private (set-name (new-name (buff 3)))
(define-private (set-name (new-name (string-ascii 3)))
(if (var-set name new-name)
new-name
(get-name)))
@@ -1357,8 +1363,8 @@ fn test_tuple_map() {
(get name (get contents (map-get? tuples (tuple (name name))))))
(add-tuple 0 \"abcde\")
(add-tuple 1 \"abcd\")
(add-tuple 0 0x0102030405)
(add-tuple 1 0x01020304)
(list (get-tuple 0)
(get-tuple 1))
";
@@ -1699,3 +1705,135 @@ fn test_set_entry_unbound_variables() {
});
}
}
#[test]
fn test_string_ascii_fold() {
let good = [
"(define-private (get-len (x (string-ascii 1)) (acc uint)) (+ acc u1))
(fold get-len \"blockstack\" u0)",
"(define-private (slice (x (string-ascii 1)) (acc (tuple (limit uint) (cursor uint) (data (string-ascii 10)))))
(if (< (get cursor acc) (get limit acc))
(let ((data (default-to (get data acc) (as-max-len? (concat (get data acc) x) u10))))
(tuple (limit (get limit acc)) (cursor (+ u1 (get cursor acc))) (data data)))
acc))
(fold slice \"blockstack\" (tuple (limit u5) (cursor u0) (data \"\")))"];
let expected = ["uint", "(tuple (cursor uint) (data (string-ascii 10)) (limit uint))"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
let type_sig = mem_type_check(good_test).unwrap().0.unwrap();
assert_eq!(expected, &type_sig.to_string());
}
}
#[test]
fn test_string_ascii_as_max_len() {
let tests = [
"(as-max-len? \"12345\" u5)",
"(as-max-len? \"12345\" u8)",
"(as-max-len? \"12345\" u4)"];
let expected = [
"(optional (string-ascii 5))",
"(optional (string-ascii 8))",
"(optional (string-ascii 4))"];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&test).unwrap()));
}
}
#[test]
fn test_string_ascii_concat() {
let good = [
"(concat \"block\" \"stack\")"];
let expected = ["(string-ascii 10)"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&good_test).unwrap()));
}
}
#[test]
fn test_string_utf8_fold() {
let good = [
"(define-private (get-len (x (string-utf8 1)) (acc uint)) (+ acc u1))
(fold get-len u\"blockstack\" u0)",
"(define-private (slice (x (string-utf8 1)) (acc (tuple (limit uint) (cursor uint) (data (string-utf8 11)))))
(if (< (get cursor acc) (get limit acc))
(let ((data (default-to (get data acc) (as-max-len? (concat (get data acc) x) u11))))
(tuple (limit (get limit acc)) (cursor (+ u1 (get cursor acc))) (data data)))
acc))
(fold slice u\"blockstack\\u{1F926}\" (tuple (limit u5) (cursor u0) (data u\"\")))"];
let expected = ["uint", "(tuple (cursor uint) (data (string-utf8 11)) (limit uint))"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
let type_sig = mem_type_check(good_test).unwrap().0.unwrap();
assert_eq!(expected, &type_sig.to_string());
}
}
#[test]
fn test_string_utf8_as_max_len() {
let tests = [
"(as-max-len? u\"1234\\u{1F926}\" u5)",
"(as-max-len? u\"1234\\u{1F926}\" u8)",
"(as-max-len? u\"1234\\u{1F926}\" u4)"];
let expected = [
"(optional (string-utf8 5))",
"(optional (string-utf8 8))",
"(optional (string-utf8 4))"];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&test).unwrap()));
}
}
#[test]
fn test_string_utf8_concat() {
let good = [
"(concat u\"block\" u\"stack\\u{1F926}\")"];
let expected = ["(string-utf8 11)"];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&good_test).unwrap()));
}
}
#[test]
fn test_buff_negative_len() {
let contract_src =
"(define-private (func (x (buff -12))) (len x))
(func 0x00)";
let res = mem_type_check(&contract_src).unwrap_err();
assert!(match &res.err {
&CheckErrors::BadSyntaxBinding => true,
_ => false
});
}
#[test]
fn test_string_ascii_negative_len() {
let contract_src =
"(define-private (func (x (string-ascii -12))) (len x))
(func \"\")";
let res = mem_type_check(&contract_src).unwrap_err();
assert!(match &res.err {
&CheckErrors::BadSyntaxBinding => true,
_ => false
});
}
#[test]
fn test_string_utf8_negative_len() {
let contract_src =
"(define-private (func (x (string-utf8 -12))) (len x))
(func u\"\")";
let res = mem_type_check(&contract_src).unwrap_err();
assert!(match &res.err {
&CheckErrors::BadSyntaxBinding => true,
_ => false
});
}

View File

@@ -44,6 +44,8 @@ pub enum ParseErrors {
TraitReferenceUnknown(String),
CommaSeparatorUnexpected,
ColonSeparatorUnexpected,
InvalidCharactersDetected,
InvalidEscaping,
}
#[derive(Debug, PartialEq)]
@@ -155,6 +157,8 @@ impl DiagnosableError for ParseErrors {
ParseErrors::TraitReferenceNotAllowed => format!("trait references can not be stored"),
ParseErrors::TraitReferenceUnknown(trait_name) => format!("use of undeclared trait <{}>", trait_name),
ParseErrors::ExpressionStackDepthTooDeep => format!("AST has too deep of an expression nesting. The maximum stack depth is {}", MAX_CALL_STACK_DEPTH),
ParseErrors::InvalidCharactersDetected => format!("invalid characters detected"),
ParseErrors::InvalidEscaping => format!("invalid escaping detected in string"),
}
}

View File

@@ -32,7 +32,7 @@ enum TokenType {
Whitespace, Comma, Colon,
LParens, RParens,
LCurly, RCurly,
StringLiteral, HexStringLiteral,
StringASCIILiteral, StringUTF8Literal, HexStringLiteral,
UIntLiteral, IntLiteral,
Variable, TraitReferenceLiteral, PrincipalLiteral,
SugaredContractIdentifierLiteral,
@@ -96,7 +96,8 @@ pub fn lex(input: &str) -> ParseResult<Vec<(LexItem, u32, u32)>> {
// it's worth either (1) an extern macro, or (2) the complexity of hand implementing.
let lex_matchers: &[LexMatcher] = &[
LexMatcher::new(r##""(?P<value>((\\")|([[ -~]&&[^"]]))*)""##, TokenType::StringLiteral),
LexMatcher::new(r##"u"(?P<value>((\\")|([[ -~]&&[^"]]))*)""##, TokenType::StringUTF8Literal),
LexMatcher::new(r##""(?P<value>((\\")|([[ -~]&&[^"]]))*)""##, TokenType::StringASCIILiteral),
LexMatcher::new(";;[ -~]*", TokenType::Whitespace), // ;; comments.
LexMatcher::new("[\n]+", TokenType::Whitespace),
LexMatcher::new("[ \t]+", TokenType::Whitespace),
@@ -107,7 +108,7 @@ pub fn lex(input: &str) -> ParseResult<Vec<(LexItem, u32, u32)>> {
LexMatcher::new("[{]", TokenType::LCurly),
LexMatcher::new("[}]", TokenType::RCurly),
LexMatcher::new("<(?P<value>([[:word:]]|[-])+)>", TokenType::TraitReferenceLiteral),
LexMatcher::new("0x(?P<value>[[:xdigit:]]+)", TokenType::HexStringLiteral),
LexMatcher::new("0x(?P<value>[[:xdigit:]]*)", TokenType::HexStringLiteral),
LexMatcher::new("u(?P<value>[[:digit:]]+)", TokenType::UIntLiteral),
LexMatcher::new("(?P<value>-?[[:digit:]]+)", TokenType::IntLiteral),
LexMatcher::new(&format!(r#"'(?P<value>{}(\.)([[:alnum:]]|[-]){{1,{}}})"#,
@@ -290,17 +291,32 @@ pub fn lex(input: &str) -> ParseResult<Vec<(LexItem, u32, u32)>> {
}?;
Ok(LexItem::LiteralValue(str_value.len(), value))
},
TokenType::StringLiteral => {
TokenType::StringASCIILiteral => {
let str_value = get_value_or_err(current_slice, captures)?;
let quote_unescaped = str_value.replace("\\\"","\"");
let slash_unescaped = quote_unescaped.replace("\\\\","\\");
let byte_vec = slash_unescaped.as_bytes().to_vec();
let value = match Value::buff_from(byte_vec) {
let str_value_len = str_value.len();
let unescaped_str = unescape_ascii_chars(str_value, false)?;
let byte_vec = unescaped_str
.as_bytes()
.to_vec();
let value = match Value::string_ascii_from_bytes(byte_vec) {
Ok(parsed) => Ok(parsed),
Err(_e) => Err(ParseError::new(ParseErrors::FailedParsingBuffer(str_value.clone())))
Err(_e) => Err(ParseError::new(ParseErrors::InvalidCharactersDetected))
}?;
Ok(LexItem::LiteralValue(str_value.len(), value))
Ok(LexItem::LiteralValue(str_value_len, value))
},
TokenType::StringUTF8Literal => {
let str_value = get_value_or_err(current_slice, captures)?;
let str_value_len = str_value.len();
let unescaped_str = unescape_ascii_chars(str_value, true)?;
let value = match Value::string_utf8_from_string_utf8_literal(unescaped_str) {
Ok(parsed) => Ok(parsed),
Err(_e) => Err(ParseError::new(ParseErrors::InvalidCharactersDetected))
}?;
Ok(LexItem::LiteralValue(str_value_len, value))
},
}?;
result.push((token, current_line, column_pos));
@@ -318,6 +334,34 @@ pub fn lex(input: &str) -> ParseResult<Vec<(LexItem, u32, u32)>> {
}
}
fn unescape_ascii_chars(escaped_str: String, allow_unicode_escape: bool) -> ParseResult<String> {
let mut unescaped_str = String::new();
let mut chars = escaped_str.chars().into_iter();
while let Some(char) = chars.next() {
if char == '\\' {
if let Some(next) = chars.next() {
match next {
// ASCII escapes based on Rust list (https://doc.rust-lang.org/reference/tokens.html#ascii-escapes)
'\\' => unescaped_str.push('\\'),
'\"' => unescaped_str.push('\"'),
'n' => unescaped_str.push('\n'),
't' => unescaped_str.push('\t'),
'r' => unescaped_str.push('\r'),
'0' => unescaped_str.push('\0'),
'u' if allow_unicode_escape == true =>
unescaped_str.push_str("\\u"),
_ => return Err(ParseError::new(ParseErrors::InvalidEscaping))
}
} else {
return Err(ParseError::new(ParseErrors::InvalidEscaping))
}
} else {
unescaped_str.push(char);
}
}
Ok(unescaped_str)
}
enum ParseStackItem {
Expression(PreSymbolicExpression),
Colon, Comma
@@ -516,8 +560,8 @@ pub fn parse(input: &str) -> ParseResult<Vec<PreSymbolicExpression>> {
#[cfg(test)]
mod test {
use vm::representations::{PreSymbolicExpression, PreSymbolicExpressionType};
use vm::{Value, ast};
use vm::types::{QualifiedContractIdentifier, PrincipalData};
use vm::ast;
use vm::types::{QualifiedContractIdentifier, PrincipalData, Value, CharType, SequenceData};
use vm::ast::errors::{ParseErrors, ParseError};
use vm::types::{TraitIdentifier};
@@ -699,8 +743,22 @@ r#"z (let ((x 1) (y 2))
let function_with_NEL = "(define (foo (x y)) \u{0085} (+ 1 2 3) \u{0085} (- 1 2 3))";
let function_with_LS = "(define (foo (x y)) \u{2028} (+ 1 2 3) \u{2028} (- 1 2 3))";
let function_with_PS = "(define (foo (x y)) \u{2029} (+ 1 2 3) \u{2029} (- 1 2 3))";
// good case
let function_with_LF = "(define (foo (x y)) \n (+ 1 2 3) \n (- 1 2 3))";
let string_with_invalid_escape = r#"
"hello\eworld"
"#;
let ascii_string_with_unicode_escape = r#"
"hello\u{1F436}world"
"#;
let string_with_valid_escape = r#"
"hello\nworld"
"#;
let string_with_valid_double_escape = r#"
"hello\\eworld"
"#;
let string_with_multiple_slashes = r#"
"hello\\\"world"
"#;
assert!(match ast::parser::parse(&split_tokens).unwrap_err().err {
ParseErrors::SeparatorExpected(_) => true, _ => false });
@@ -773,6 +831,20 @@ r#"z (let ((x 1) (y 2))
ParseErrors::FailedParsingRemainder(_) => true, _ => false });
ast::parser::parse(&function_with_LF).unwrap();
}
}
assert!(match ast::parser::parse(&string_with_invalid_escape).unwrap_err().err {
ParseErrors::InvalidEscaping => true, _ => false });
assert!(match ast::parser::parse(&ascii_string_with_unicode_escape).unwrap_err().err {
ParseErrors::InvalidEscaping => true, _ => false });
assert!(match ast::parser::parse(&string_with_valid_escape).unwrap()[0].pre_expr {
PreSymbolicExpressionType::AtomValue(Value::Sequence(SequenceData::String(CharType::ASCII(ref v)))) if v.data.len() == 11 => true, _ => false });
assert!(match ast::parser::parse(&string_with_valid_double_escape).unwrap()[0].pre_expr {
PreSymbolicExpressionType::AtomValue(Value::Sequence(SequenceData::String(CharType::ASCII(ref v)))) if v.data.len() == 12 => true, _ => false });
assert!(match ast::parser::parse(&string_with_multiple_slashes).unwrap()[0].pre_expr {
PreSymbolicExpressionType::AtomValue(Value::Sequence(SequenceData::String(CharType::ASCII(ref v)))) if v.data.len() == 12 => true, _ => false });
}
}

View File

@@ -394,10 +394,8 @@ has to be a literal function name.",
(fold * (list 2 2 2) 0) ;; Returns 0
;; calculates (- 11 (- 7 (- 3 2)))
(fold - (list 3 7 11) 2) ;; Returns 5
(fold concat \"cdef\" \"ab\") ;; Returns 0x666564636162
;; hex form of \"fedcab\"
(fold concat (list \"cd\" \"ef\") \"ab\") ;; Returns 0x656663646162
;; hex form of \"efcdab\""
(fold concat \"cdef\" \"ab\") ;; Returns \"fedcab\"
(fold concat (list \"cd\" \"ef\") \"ab\") ;; Returns \"efcdab\""
};
const CONCAT_API: SpecialAPI = SpecialAPI {
@@ -406,8 +404,7 @@ const CONCAT_API: SpecialAPI = SpecialAPI {
signature: "(concat buff-a buff-b)",
description: "The `concat` function takes two buffers or two lists with the same entry type,
and returns a concatenated buffer or list of the same entry type, with max_len = max_len_a + max_len_b.",
example: "(concat \"hello \" \"world\") ;; Returns 0x68656c6c6f20776f726c64
;; hex form of \"hello world\""
example: "(concat \"hello \" \"world\") ;; Returns \"hello world\""
};
const APPEND_API: SpecialAPI = SpecialAPI {
@@ -426,8 +423,8 @@ const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI {
description: "The `as-max-len?` function takes a length N (must be a literal) and a buffer or list argument, which must be typed as a list
or buffer of length M and outputs that same list or buffer, but typed with max length N.
This function returns an optional type with the resulting iterable. If the input iterable is less than
or equal to the supplied max-len, it returns `(some <iterable>)`, otherwise it returns `none`.",
This function returns an optional type with the resulting sequence. If the input sequence is less than
or equal to the supplied max-len, it returns `(some <sequence>)`, otherwise it returns `none`.",
example: "(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2))
(as-max-len? (list 1 2 3) u2) ;; Returns none"
};
@@ -477,7 +474,7 @@ const FETCH_ENTRY_API: SpecialAPI = SpecialAPI {
The value is looked up using `key-tuple`.
If there is no value associated with that key in the data map, the function returns a `none` option. Otherwise,
it returns `(some value)`.",
example: "(define-map names-map ((name (buff 10))) ((id int)))
example: "(define-map names-map ((name (string-ascii 10))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(map-get? names-map (tuple (name \"blockstack\"))) ;; Returns (some (tuple (id 1337)))
(map-get? names-map ((name \"blockstack\"))) ;; Same command, using a shorthand for constructing the tuple
@@ -494,7 +491,7 @@ with the key, the function overwrites that existing association.
Note: the `value-tuple` requires 1 additional byte for storage in the materialized blockchain state,
and therefore the maximum size of a value that may be inserted into a map is MAX_CLARITY_VALUE - 1.",
example: "(define-map names-map ((name (buff 10))) ((id int)))
example: "(define-map names-map ((name (string-ascii 10))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 }) ;; Returns true
(map-set names-map ((name \"blockstack\")) ((id 1337))) ;; Same command, using a shorthand for constructing the tuple
",
@@ -511,7 +508,7 @@ this key in the data map, the function returns `false`.
Note: the `value-tuple` requires 1 additional byte for storage in the materialized blockchain state,
and therefore the maximum size of a value that may be inserted into a map is MAX_CLARITY_VALUE - 1.",
example: "(define-map names-map ((name (buff 10))) ((id int)))
example: "(define-map names-map ((name (string-ascii 10))) ((id int)))
(map-insert names-map { name: \"blockstack\" } { id: 1337 }) ;; Returns true
(map-insert names-map { name: \"blockstack\" } { id: 1337 }) ;; Returns false
(map-insert names-map ((name \"blockstack\")) ((id 1337))) ;; Same command, using a shorthand for constructing the tuple
@@ -525,7 +522,7 @@ const DELETE_ENTRY_API: SpecialAPI = SpecialAPI {
description: "The `map-delete` function removes the value associated with the input key for
the given map. If an item exists and is removed, the function returns `true`.
If a value did not exist for this key in the data map, the function returns `false`.",
example: "(define-map names-map ((name (buff 10))) ((id int)))
example: "(define-map names-map ((name (string-ascii 10))) ((id int)))
(map-insert names-map { name: \"blockstack\" } { id: 1337 }) ;; Returns true
(map-delete names-map { name: \"blockstack\" }) ;; Returns true
(map-delete names-map { name: \"blockstack\" }) ;; Returns false
@@ -551,7 +548,7 @@ const TUPLE_GET_API: SpecialAPI = SpecialAPI {
description: "The `get` function fetches the value associated with a given key from the supplied typed tuple.
If an `Optional` value is supplied as the inputted tuple, `get` returns an `Optional` type of the specified key in
the tuple. If the supplied option is a `(none)` option, get returns `(none)`.",
example: "(define-map names-map ((name (buff 12))) ((id int)))
example: "(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-insert names-map { name: \"blockstack\" } { id: 1337 }) ;; Returns true
(get id (tuple (name \"blockstack\") (id 1337))) ;; Returns 1337
(get id (map-get? names-map (tuple (name \"blockstack\")))) ;; Returns (some 1337)
@@ -686,9 +683,9 @@ option. If the argument is a response type, and the argument is an `(ok ...)` re
the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `(none)` value,
`unwrap!` _returns_ `thrown-value` from the current function and exits the current control-flow.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(define-private (get-name-or-err (name (buff 12)))
(define-private (get-name-or-err (name (string-ascii 12)))
(let ((raw-name (unwrap! (map-get? names-map { name: name }) (err 1))))
(ok raw-name)))
@@ -706,7 +703,7 @@ option. If the argument is a response type, and the argument is an `(ok ...)` re
the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `none` value,
`try!` _returns_ either `none` or the `(err ...)` value from the current function and exits the current control-flow.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(try! (map-get? names-map { name: \"blockstack\" })) ;; Returns (tuple (id 1337))
(define-private (checked-even (x int))
@@ -730,7 +727,7 @@ option. If the argument is a response type, and the argument is an `(ok ...)` re
the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `(none)` value,
`unwrap` throws a runtime error, aborting any further processing of the current transaction.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(unwrap-panic (map-get? names-map { name: \"blockstack\" })) ;; Returns (tuple (id 1337))
(unwrap-panic (map-get? names-map { name: \"non-existant\" })) ;; Throws a runtime exception
@@ -799,7 +796,7 @@ is untyped, you should use `unwrap-panic` or `unwrap-err-panic` instead of `matc
(add-10 (some 5)) ;; returns 15
(add-10 none) ;; returns 10
(define-private (add-or-pass-err (x (response int (buff 10))) (to-add int))
(define-private (add-or-pass-err (x (response int (string-ascii 10))) (to-add int))
(match x
value (+ to-add value)
err-value (err err-value)))
@@ -815,7 +812,7 @@ const DEFAULT_TO_API: SpecialAPI = SpecialAPI {
a `(some ...)` option, it returns the inner value of the option. If the second argument is a `(none)` value,
`default-to` it returns the value of `default-value`.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(default-to 0 (get id (map-get? names-map (tuple (name \"blockstack\"))))) ;; Returns 1337
(default-to 0 (get id (map-get? names-map (tuple (name \"non-existant\"))))) ;; Returns 0
@@ -868,7 +865,7 @@ const IS_NONE_API: SpecialAPI = SpecialAPI {
description: "`is-none` tests a supplied option value, returning `true` if the option value is `(none)`,
and `false` if it is a `(some ...)`.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(is-none (get id (map-get? names-map { name: \"blockstack\" }))) ;; Returns false
(is-none (get id (map-get? names-map { name: \"non-existant\" }))) ;; Returns true"
@@ -891,7 +888,7 @@ const IS_SOME_API: SpecialAPI = SpecialAPI {
description: "`is-some` tests a supplied option value, returning `true` if the option value is `(some ...)`,
and `false` if it is a `none`.",
example: "
(define-map names-map ((name (buff 12))) ((id int)))
(define-map names-map ((name (string-ascii 12))) ((id int)))
(map-set names-map { name: \"blockstack\" } { id: 1337 })
(is-some (get id (map-get? names-map { name: \"blockstack\" }))) ;; Returns true
(is-some (get id (map-get? names-map { name: \"non-existant\" }))) ;; Returns false"
@@ -1176,7 +1173,7 @@ If an asset identified by `asset-identifier` _already exists_, this function wil
Otherwise, on successfuly mint, it returns `(ok true)`.
",
example: "
(define-non-fungible-token stackaroo (buff 40))
(define-non-fungible-token stackaroo (string-ascii 40))
(nft-mint? stackaroo \"Roo\" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; returns (ok true)
"
};
@@ -1189,7 +1186,7 @@ const GET_OWNER: SpecialAPI = SpecialAPI {
The asset type must have been defined using `define-non-fungible-token`, and the supplied `asset-identifier` must be of the same type specified in
that definition.",
example: "
(define-non-fungible-token stackaroo (buff 40))
(define-non-fungible-token stackaroo (string-ascii 40))
(nft-mint? stackaroo \"Roo\" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)
(nft-get-owner? stackaroo \"Roo\") ;; Returns (some SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)
(nft-get-owner? stackaroo \"Too\") ;; Returns none
@@ -1248,7 +1245,7 @@ one of the following error codes:
`(err u3)` -- asset identified by asset-identifier does not exist
",
example: "
(define-non-fungible-token stackaroo (buff 40))
(define-non-fungible-token stackaroo (string-ascii 40))
(nft-mint? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)
(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; returns (ok true)
(nft-transfer? stackaroo \"Roo\" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; returns (err u1)

View File

@@ -4,7 +4,7 @@ use std::cmp;
use vm::functions::tuples;
use vm::functions::tuples::TupleDefinitionType::{Implicit, Explicit};
use vm::types::{Value, OptionalData, BuffData, PrincipalData, BlockInfoProperty, TypeSignature, BUFF_32};
use vm::types::{Value, SequenceData, OptionalData, BuffData, PrincipalData, BlockInfoProperty, TypeSignature, BUFF_32};
use vm::representations::{SymbolicExpression, SymbolicExpressionType};
use vm::errors::{CheckErrors, InterpreterError, RuntimeErrorType, InterpreterResult as Result,
check_argument_count, check_arguments_at_least};
@@ -192,7 +192,7 @@ pub fn special_at_block(args: &[SymbolicExpression],
runtime_cost!(cost_functions::AT_BLOCK, env, 0)?;
let bhh = match eval(&args[0], env, &context)? {
Value::Buffer(BuffData { data }) => {
Value::Sequence(SequenceData::Buffer(BuffData { data })) => {
if data.len() != 32 {
return Err(RuntimeErrorType::BadBlockHash(data).into())
} else {
@@ -353,19 +353,19 @@ pub fn special_get_block_info(args: &[SymbolicExpression],
},
BlockInfoProperty::VrfSeed => {
let vrf_seed = env.global_context.database.get_block_vrf_seed(height_value);
Value::Buffer(BuffData { data: vrf_seed.as_bytes().to_vec() })
Value::Sequence(SequenceData::Buffer(BuffData { data: vrf_seed.as_bytes().to_vec() }))
},
BlockInfoProperty::HeaderHash => {
let header_hash = env.global_context.database.get_block_header_hash(height_value);
Value::Buffer(BuffData { data: header_hash.as_bytes().to_vec() })
Value::Sequence(SequenceData::Buffer(BuffData { data: header_hash.as_bytes().to_vec() }))
},
BlockInfoProperty::BurnchainHeaderHash => {
let burnchain_header_hash = env.global_context.database.get_burnchain_block_header_hash(height_value);
Value::Buffer(BuffData { data: burnchain_header_hash.as_bytes().to_vec() })
Value::Sequence(SequenceData::Buffer(BuffData { data: burnchain_header_hash.as_bytes().to_vec() }))
},
BlockInfoProperty::IdentityHeaderHash => {
let id_header_hash = env.global_context.database.get_index_block_header_hash(height_value);
Value::Buffer(BuffData { data: id_header_hash.as_bytes().to_vec() })
Value::Sequence(SequenceData::Buffer(BuffData { data: id_header_hash.as_bytes().to_vec() }))
},
BlockInfoProperty::MinerAddress => {
let miner_address = env.global_context.database.get_miner_address(height_value);

View File

@@ -1,219 +0,0 @@
use vm::costs::{cost_functions, CostOverflowingMath};
use vm::errors::{CheckErrors, RuntimeErrorType, InterpreterResult as Result, check_argument_count};
use vm::types::{Value, ListData, signatures::ListTypeData, TypeSignature::BoolType, TypeSignature};
use vm::representations::{SymbolicExpression, SymbolicExpressionType};
use vm::{LocalContext, Environment, eval, apply, lookup_function};
use std::convert::TryInto;
use std::cmp;
pub fn list_cons(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
let eval_tried: Result<Vec<Value>> =
args.iter().map(|x| eval(x, env, context)).collect();
let args = eval_tried?;
let mut arg_size = 0;
for a in args.iter() {
arg_size = arg_size.cost_overflow_add(a.size().into())?;
}
runtime_cost!(cost_functions::LIST_CONS, env, arg_size)?;
Value::list_from(args)
}
pub fn special_filter(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
runtime_cost!(cost_functions::FILTER, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let function = lookup_function(&function_name, env)?;
let iterable = eval(&args[1], env, context)?;
match iterable {
Value::List(mut list) => {
let mut filtered_vec = Vec::new();
for x in list.data.drain(..) {
let argument = [ SymbolicExpression::atom_value(x.clone()) ];
let filter_eval = apply(&function, &argument, env, context)?;
if let Value::Bool(include) = filter_eval {
if include {
filtered_vec.push(x);
} // else, filter out.
} else {
return Err(CheckErrors::TypeValueError(BoolType, filter_eval).into())
}
}
Value::list_with_type(filtered_vec, list.type_signature)
},
Value::Buffer(mut buff) => {
let mut filtered_vec = Vec::new();
for x in buff.data.drain(..) {
let v = Value::buff_from(vec![x.clone()])?;
let argument = [ SymbolicExpression::atom_value(v) ];
let filter_eval = apply(&function, &argument, env, context)?;
if let Value::Bool(include) = filter_eval {
if include {
filtered_vec.push(x);
} // else, filter out.
} else {
return Err(CheckErrors::TypeValueError(BoolType, filter_eval).into())
}
}
Value::buff_from(filtered_vec)
},
_ => Err(CheckErrors::ExpectedListOrBuffer(TypeSignature::type_of(&iterable)).into())
}
}
pub fn special_fold(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(3, args)?;
runtime_cost!(cost_functions::FILTER, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let function = lookup_function(&function_name, env)?;
let iterable = eval(&args[1], env, context)?;
let initial = eval(&args[2], env, context)?;
let mapped_args: Vec<_> = match iterable {
Value::List(mut list) => {
list.data.drain(..).map(|x| {
SymbolicExpression::atom_value(x)
}).collect()
},
Value::Buffer(mut buff) => {
buff.data.drain(..).map(|x| {
SymbolicExpression::atom_value(Value::buff_from_byte(x))
}).collect()
},
_ => return Err(CheckErrors::ExpectedListOrBuffer(TypeSignature::type_of(&iterable)).into())
};
mapped_args.iter().try_fold(initial, |acc, x| {
apply(&function, &[x.clone(), SymbolicExpression::atom_value(acc)], env, context)
})
}
pub fn special_map(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
runtime_cost!(cost_functions::MAP, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let iterable = eval(&args[1], env, context)?;
let function = lookup_function(&function_name, env)?;
let mapped_args: Vec<_> = match iterable {
Value::List(mut list) => {
list.data.drain(..).map(|x| {
vec![SymbolicExpression::atom_value(x)]
}).collect()
},
Value::Buffer(mut buff) => {
buff.data.drain(..).map(|x| {
vec![SymbolicExpression::atom_value(Value::buff_from_byte(x))]
}).collect()
},
_ => return Err(CheckErrors::ExpectedListOrBuffer(TypeSignature::type_of(&iterable)).into())
};
let mapped_vec: Result<Vec<_>> =
mapped_args.iter().map(|argument| apply(&function, &argument, env, context)).collect();
Value::list_from(mapped_vec?)
}
pub fn special_append(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let iterable = eval(&args[0], env, context)?;
match iterable {
Value::List(list) => {
let element = eval(&args[1], env, context)?;
let ListData { mut data, type_signature } = list;
let (entry_type, size) = type_signature.destruct();
let element_type = TypeSignature::type_of(&element);
runtime_cost!(cost_functions::APPEND, env,
u64::from(cmp::max(entry_type.size(), element_type.size())))?;
if entry_type.is_no_type() {
assert_eq!(size, 0);
return Value::list_from(vec![ element ])
}
if let Ok(next_entry_type) = TypeSignature::least_supertype(&entry_type, &element_type) {
let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1)?;
data.push(element);
Ok(Value::List(ListData {
type_signature: next_type_signature,
data }))
} else {
Err(CheckErrors::TypeValueError(entry_type, element).into())
}
},
_ => Err(CheckErrors::ExpectedListApplication.into())
}
}
pub fn special_concat(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let lhs = eval(&args[0], env, context)?;
let rhs = eval(&args[1], env, context)?;
runtime_cost!(cost_functions::CONCAT, env,
u64::from(lhs.size()).cost_overflow_add(
u64::from(rhs.size()))?)?;
match (lhs, rhs) {
(Value::List(lhs_data), Value::List(mut rhs_data)) => {
let mut data = lhs_data.data;
data.append(&mut rhs_data.data);
Value::list_from(data)
},
(Value::Buffer(lhs_data), Value::Buffer(mut rhs_data)) => {
let mut data = lhs_data.data;
data.append(&mut rhs_data.data);
Value::buff_from(data)
},
(_, _) => {
Err(RuntimeErrorType::BadTypeConstruction.into())
}
}
}
pub fn special_as_max_len(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let mut iterable = eval(&args[0], env, context)?;
runtime_cost!(cost_functions::AS_MAX_LEN, env, 0)?;
if let Some(Value::UInt(expected_len)) = args[1].match_literal_value() {
let iterable_len = match iterable {
Value::List(ref list) => list.data.len(),
Value::Buffer(ref buff) => buff.data.len(),
_ => return Err(CheckErrors::ExpectedListOrBuffer(TypeSignature::type_of(&iterable)).into())
};
if iterable_len as u128 > *expected_len {
Ok(Value::none())
} else {
if let Value::List(ref mut list) = iterable {
list.type_signature.reduce_max_len(*expected_len as u32);
}
Ok(Value::some(iterable)?)
}
} else {
let actual_len = eval(&args[1], env, context)?;
Err(CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::type_of(&actual_len)).into())
}
}
pub fn native_len(iterable: Value) -> Result<Value> {
match iterable {
Value::List(list) => Ok(Value::UInt(list.data.len() as u128)),
Value::Buffer(buff) => Ok(Value::UInt(buff.data.len() as u128)),
_ => Err(CheckErrors::ExpectedListOrBuffer(TypeSignature::type_of(&iterable)).into())
}
}

View File

@@ -1,6 +1,6 @@
pub mod define;
pub mod tuples;
mod iterables;
mod sequences;
mod arithmetic;
mod boolean;
mod database;
@@ -8,7 +8,7 @@ mod options;
mod assets;
use vm::errors::{Error, CheckErrors, RuntimeErrorType, ShortReturnType, InterpreterResult as Result, check_argument_count, check_arguments_at_least};
use vm::types::{Value, PrincipalData, ResponseData, TypeSignature};
use vm::types::{Value, SequenceData, CharType, PrincipalData, ResponseData, TypeSignature};
use vm::callables::{CallableType, NativeHandle};
use vm::representations::{SymbolicExpression, SymbolicExpressionType, ClarityName};
use vm::representations::SymbolicExpressionType::{List, Atom};
@@ -116,14 +116,14 @@ pub fn lookup_reserved_functions(name: &str) -> Option<CallableType> {
Let => SpecialFunction("special_let", &special_let),
FetchVar => SpecialFunction("special_var-get", &database::special_fetch_variable),
SetVar => SpecialFunction("special_set-var", &database::special_set_variable),
Map => SpecialFunction("special_map", &iterables::special_map),
Filter => SpecialFunction("special_filter", &iterables::special_filter),
Fold => SpecialFunction("special_fold", &iterables::special_fold),
Concat => SpecialFunction("special_concat", &iterables::special_concat),
AsMaxLen => SpecialFunction("special_as_max_len", &iterables::special_as_max_len),
Append => SpecialFunction("special_append", &iterables::special_append),
Len => NativeFunction("native_len", NativeHandle::SingleArg(&iterables::native_len), cost_functions::LEN),
ListCons => SpecialFunction("special_list_cons", &iterables::list_cons),
Map => SpecialFunction("special_map", &sequences::special_map),
Filter => SpecialFunction("special_filter", &sequences::special_filter),
Fold => SpecialFunction("special_fold", &sequences::special_fold),
Concat => SpecialFunction("special_concat", &sequences::special_concat),
AsMaxLen => SpecialFunction("special_as_max_len", &sequences::special_as_max_len),
Append => SpecialFunction("special_append", &sequences::special_append),
Len => NativeFunction("native_len", NativeHandle::SingleArg(&sequences::native_len), cost_functions::LEN),
ListCons => SpecialFunction("special_list_cons", &sequences::list_cons),
FetchEntry => SpecialFunction("special_map-get?", &database::special_fetch_entry),
SetEntry => SpecialFunction("special_set-entry", &database::special_set_entry),
InsertEntry => SpecialFunction("special_insert-entry", &database::special_insert_entry),
@@ -200,7 +200,7 @@ macro_rules! native_hash_func {
let bytes = match input {
Value::Int(value) => Ok(value.to_le_bytes().to_vec()),
Value::UInt(value) => Ok(value.to_le_bytes().to_vec()),
Value::Buffer(value) => Ok(value.data),
Value::Sequence(SequenceData::Buffer(value)) => Ok(value.data),
_ => Err(CheckErrors::UnionTypeValueError(vec![TypeSignature::IntType, TypeSignature::UIntType, TypeSignature::max_buffer()], input))
}?;
let hash = <$module>::from_data(&bytes);

View File

@@ -0,0 +1,177 @@
use vm::costs::{cost_functions, CostOverflowingMath};
use vm::errors::{CheckErrors, RuntimeErrorType, InterpreterResult as Result, check_argument_count};
use vm::types::{Value, SequenceData, CharType, ListData, signatures::ListTypeData, TypeSignature::BoolType, TypeSignature};
use vm::representations::{SymbolicExpression, SymbolicExpressionType};
use vm::{LocalContext, Environment, CallableType, eval, apply, lookup_function};
use std::convert::TryInto;
use std::cmp;
pub fn list_cons(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
let eval_tried: Result<Vec<Value>> =
args.iter().map(|x| eval(x, env, context)).collect();
let args = eval_tried?;
let mut arg_size = 0;
for a in args.iter() {
arg_size = arg_size.cost_overflow_add(a.size().into())?;
}
runtime_cost!(cost_functions::LIST_CONS, env, arg_size)?;
Value::list_from(args)
}
pub fn special_filter(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
runtime_cost!(cost_functions::FILTER, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let mut sequence = eval(&args[1], env, context)?;
let function = lookup_function(&function_name, env)?;
match sequence {
Value::Sequence(ref mut sequence_data) => {
sequence_data.filter(&mut |atom_value: SymbolicExpression| {
let argument = [atom_value];
let filter_eval = apply(&function, &argument, env, context)?;
if let Value::Bool(include) = filter_eval {
return Ok(include);
} else {
return Err(CheckErrors::TypeValueError(BoolType, filter_eval).into())
}
})?;
},
_ => return Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)).into())
};
Ok(sequence)
}
pub fn special_fold(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(3, args)?;
runtime_cost!(cost_functions::FOLD, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let function = lookup_function(&function_name, env)?;
let mut sequence = eval(&args[1], env, context)?;
let initial = eval(&args[2], env, context)?;
match sequence {
Value::Sequence(ref mut sequence_data) => {
sequence_data.atom_values()
.into_iter()
.try_fold(initial, |acc, x| {
apply(&function, &[x, SymbolicExpression::atom_value(acc)], env, context)
})
},
_ => Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)).into())
}
}
pub fn special_map(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
runtime_cost!(cost_functions::MAP, env, 0)?;
let function_name = args[0].match_atom()
.ok_or(CheckErrors::ExpectedName)?;
let mut sequence = eval(&args[1], env, context)?;
let function = lookup_function(&function_name, env)?;
let mapped_sequence: Vec<_> = match sequence {
Value::Sequence(ref mut sequence_data) => {
sequence_data.atom_values()
.into_iter()
.map(|argument| apply(&function, &[argument], env, context))
.collect()
},
_ => Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)).into())
}?;
Value::list_from(mapped_sequence)
}
pub fn special_append(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let sequence = eval(&args[0], env, context)?;
match sequence {
Value::Sequence(SequenceData::List(list)) => {
let element = eval(&args[1], env, context)?;
let ListData { mut data, type_signature } = list;
let (entry_type, size) = type_signature.destruct();
let element_type = TypeSignature::type_of(&element);
runtime_cost!(cost_functions::APPEND, env,
u64::from(cmp::max(entry_type.size(), element_type.size())))?;
if entry_type.is_no_type() {
assert_eq!(size, 0);
return Value::list_from(vec![ element ])
}
if let Ok(next_entry_type) = TypeSignature::least_supertype(&entry_type, &element_type) {
let next_type_signature = ListTypeData::new_list(next_entry_type, size + 1)?;
data.push(element);
Ok(Value::Sequence(SequenceData::List(ListData {
type_signature: next_type_signature,
data })))
} else {
Err(CheckErrors::TypeValueError(entry_type, element).into())
}
},
_ => Err(CheckErrors::ExpectedListApplication.into())
}
}
pub fn special_concat(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let mut wrapped_seq = eval(&args[0], env, context)?;
let mut other_wrapped_seq = eval(&args[1], env, context)?;
runtime_cost!(cost_functions::CONCAT, env,
u64::from(wrapped_seq.size()).cost_overflow_add(
u64::from(other_wrapped_seq.size()))?)?;
match (&mut wrapped_seq, &mut other_wrapped_seq) {
(Value::Sequence(ref mut seq), Value::Sequence(ref mut other_seq)) => seq.append(other_seq),
_ => Err(RuntimeErrorType::BadTypeConstruction.into())
}?;
Ok(wrapped_seq)
}
pub fn special_as_max_len(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
check_argument_count(2, args)?;
let mut sequence = eval(&args[0], env, context)?;
runtime_cost!(cost_functions::AS_MAX_LEN, env, 0)?;
if let Some(Value::UInt(expected_len)) = args[1].match_literal_value() {
let sequence_len = match sequence {
Value::Sequence(ref sequence_data) => sequence_data.len() as u128,
_ => return Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)).into())
};
if sequence_len > *expected_len {
Ok(Value::none())
} else {
if let Value::Sequence(SequenceData::List(ref mut list)) = sequence {
list.type_signature.reduce_max_len(*expected_len as u32);
}
Ok(Value::some(sequence)?)
}
} else {
let actual_len = eval(&args[1], env, context)?;
Err(CheckErrors::TypeError(TypeSignature::UIntType, TypeSignature::type_of(&actual_len)).into())
}
}
pub fn native_len(sequence: Value) -> Result<Value> {
match sequence {
Value::Sequence(sequence_data) => Ok(Value::UInt(sequence_data.len() as u128)),
_ => Err(CheckErrors::ExpectedSequence(TypeSignature::type_of(&sequence)).into())
}
}

View File

@@ -1,6 +1,6 @@
use std::convert::TryFrom;
use vm::errors::{Error, CheckErrors, RuntimeErrorType, ShortReturnType};
use vm::types::{Value, TupleData, TypeSignature, QualifiedContractIdentifier, StandardPrincipalData, ListData, TupleTypeSignature};
use vm::types::{Value, SequenceData, TupleData, TypeSignature, QualifiedContractIdentifier, StandardPrincipalData, ListData, TupleTypeSignature};
use vm::contexts::{OwnedEnvironment};
use vm::database::MemoryBackingStore;
use vm::execute;
@@ -307,7 +307,7 @@ fn test_get_list_max_len() {
let actual_value = execute(&contract_src).unwrap().unwrap();
match actual_value {
Value::List(ListData { data, type_signature }) => {
Value::Sequence(SequenceData::List(ListData { data, type_signature })) => {
assert_eq!(vec![Value::Int(1), Value::Int(2), Value::Int(3)],
data);
assert_eq!("(list 10 int)", &format!("{}", TypeSignature::from(type_signature)));
@@ -317,12 +317,12 @@ fn test_get_list_max_len() {
}
#[test]
fn test_set_buffer_variable() {
fn test_set_string_variable() {
let contract_src = r#"
(define-data-var name (buff 5) "alice")
(define-data-var name (string-ascii 5) "alice")
(define-private (get-name)
(var-get name))
(define-private (set-name (new-name (buff 5)))
(define-private (set-name (new-name (string-ascii 5)))
(if (var-set name new-name)
new-name
(get-name)))
@@ -331,9 +331,9 @@ fn test_set_buffer_variable() {
let mut contract_src = contract_src.to_string();
contract_src.push_str("(list (get-name) (set-name \"celia\") (get-name))");
let expected = Value::list_from(vec![
Value::buff_from("alice".to_string().into_bytes()).unwrap(),
Value::buff_from("celia".to_string().into_bytes()).unwrap(),
Value::buff_from("celia".to_string().into_bytes()).unwrap(),
Value::string_ascii_from_bytes("alice".to_string().into_bytes()).unwrap(),
Value::string_ascii_from_bytes("celia".to_string().into_bytes()).unwrap(),
Value::string_ascii_from_bytes("celia".to_string().into_bytes()).unwrap(),
]);
assert_executes(expected, &contract_src);
}
@@ -528,10 +528,10 @@ fn lists_system() {
fn tuples_system() {
let test1 =
"(define-map tuples ((name int))
((contents (tuple (name (buff 5))
(owner (buff 5))))))
((contents (tuple (name (string-ascii 5))
(owner (string-ascii 5))))))
(define-private (add-tuple (name int) (content (buff 5)))
(define-private (add-tuple (name int) (content (string-ascii 5)))
(map-insert tuples (tuple (name name))
(tuple (contents
(tuple (name content)
@@ -565,8 +565,8 @@ fn tuples_system() {
test_bad_tuple_5.push_str("(map-delete tuples (tuple (names 1)))");
let expected = || {
let buff1 = Value::buff_from("abcde".to_string().into_bytes())?;
let buff2 = Value::buff_from("abcd".to_string().into_bytes())?;
let buff1 = Value::string_ascii_from_bytes("abcde".to_string().into_bytes())?;
let buff2 = Value::string_ascii_from_bytes("abcd".to_string().into_bytes())?;
Value::list_from(vec![buff1, buff2])
};

View File

@@ -36,7 +36,7 @@ pub fn rollback_log_memory_test() {
&StacksBlockId([0 as u8; 32]),
&NULL_HEADER_DB);
let define_data_var = "(define-data-var XZ (buff 1048576) \"a\")";
let define_data_var = "(define-data-var XZ (buff 1048576) 0x00)";
let mut contract = define_data_var.to_string();
for i in 0..20 {
@@ -78,7 +78,7 @@ pub fn let_memory_test() {
&StacksBlockId([0 as u8; 32]),
&NULL_HEADER_DB);
let define_data_var = "(define-constant buff-0 \"a\")";
let define_data_var = "(define-constant buff-0 0x00)";
let mut contract = define_data_var.to_string();
for i in 0..20 {
@@ -121,7 +121,7 @@ pub fn argument_memory_test() {
&StacksBlockId([0 as u8; 32]),
&NULL_HEADER_DB);
let define_data_var = "(define-constant buff-0 \"a\")";
let define_data_var = "(define-constant buff-0 0x00)";
let mut contract = define_data_var.to_string();
for i in 0..20 {
@@ -165,7 +165,7 @@ pub fn fcall_memory_test() {
&StacksBlockId([0 as u8; 32]),
&NULL_HEADER_DB);
let define_data_var = "(define-constant buff-0 \"a\")";
let define_data_var = "(define-constant buff-0 0x00)";
let mut contract = define_data_var.to_string();
for i in 0..20 {
@@ -237,7 +237,7 @@ pub fn ccall_memory_test() {
&StacksBlockId([0 as u8; 32]),
&NULL_HEADER_DB);
let define_data_var = "(define-constant buff-0 \"a\")\n";
let define_data_var = "(define-constant buff-0 0x00)\n";
let mut contract = define_data_var.to_string();
for i in 0..20 {

View File

@@ -15,7 +15,7 @@ use chainstate::stacks::StacksBlockId;
mod forking;
mod assets;
mod events;
mod iterables;
mod sequences;
mod defines;
mod simple_apply_eval;
mod datamaps;

View File

@@ -1,6 +1,6 @@
use vm::types::{Value, TypeSignature};
use vm::types::TypeSignature::{IntType, UIntType, BoolType, ListType, BufferType};
use vm::types::signatures::{ListTypeData};
use vm::types::TypeSignature::{IntType, UIntType, BoolType, SequenceType};
use vm::types::signatures::{ListTypeData, SequenceSubtype};
use vm::execute;
use vm::errors::{CheckErrors, RuntimeErrorType, Error};
@@ -35,6 +35,177 @@ fn test_simple_list_admission() {
});
}
#[test]
fn test_string_ascii_admission() {
let defines =
"(define-private (set-name (x (string-ascii 11))) x)";
let t1 = format!("{} (set-name \"hello world\")", defines);
let expected = Value::string_ascii_from_bytes("hello world".into()).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_admission() {
let defines =
"(define-private (set-name (x (string-utf8 14))) x)";
let t1 = format!("{} (set-name u\"my 2 \\u{{c2a2}} (cents)\")", defines);
let expected = Value::string_utf8_from_string_utf8_literal("my 2 \\u{c2a2} (cents)".into()).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_map() {
let defines =
"(define-private (replace-a-with-b (c (string-ascii 1))) (if (is-eq \"a\" c) \"b\" c))";
let t1 = format!("{} (map replace-a-with-b \"ababab\")", defines);
let expected = Value::list_from(vec![
Value::string_ascii_from_bytes("b".into()).unwrap(),
Value::string_ascii_from_bytes("b".into()).unwrap(),
Value::string_ascii_from_bytes("b".into()).unwrap(),
Value::string_ascii_from_bytes("b".into()).unwrap(),
Value::string_ascii_from_bytes("b".into()).unwrap(),
Value::string_ascii_from_bytes("b".into()).unwrap(),
]).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_map() {
let defines =
"(define-private (replace-dog-with-fox (c (string-utf8 1))) (if (is-eq u\"\\u{1F436}\" c) u\"\\u{1F98A}\" c))";
let t1 = format!("{} (map replace-dog-with-fox u\"fox \\u{{1F436}}\")", defines);
let expected = Value::list_from(vec![
Value::string_utf8_from_bytes("f".into()).unwrap(),
Value::string_utf8_from_bytes("o".into()).unwrap(),
Value::string_utf8_from_bytes("x".into()).unwrap(),
Value::string_utf8_from_bytes(" ".into()).unwrap(),
Value::string_utf8_from_bytes("🦊".into()).unwrap(),
]).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_filter() {
let defines =
"(define-private (remove-a (c (string-ascii 1))) (not (is-eq \"a\" c)))";
let t1 = format!("{} (filter remove-a \"ababab\")", defines);
let expected = Value::string_ascii_from_bytes("bbb".into()).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_filter() {
let defines =
"(define-private (keep-dog (c (string-utf8 1))) (is-eq u\"\\u{1F436}\" c))";
let t1 = format!("{} (filter keep-dog u\"fox \\u{{1F98A}} \\u{{1F436}}\")", defines);
let expected = Value::string_utf8_from_bytes("🐶".into()).unwrap();
assert_eq!(expected, execute(&t1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_fold() {
let test1 =
"(define-private (merge (x (string-ascii 1)) (acc (string-ascii 5))) (concat acc x))
(fold merge (list \"A\" \"B\" \"C\" \"D\" \"E\") \"\")";
let expected = Value::string_ascii_from_bytes("ABCDE".into()).unwrap();
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_fold() {
let test1 =
"(define-private (build-face-palm (x (string-utf8 1)) (acc (string-utf8 5))) (concat acc x))
(fold build-face-palm (list u\"\\u{1F926}\" u\"\\u{1F3FC}\" u\"\\u{200D}\" u\"\\u{2642}\" u\"\\u{FE0F}\") u\"\")";
let expected = Value::string_utf8_from_bytes("🤦🏼‍♂️".into()).unwrap();
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_concat() {
let test1 = "(concat (concat \"A\" \"B\") \"C\")";
let expected = Value::string_ascii_from_bytes("ABC".into()).unwrap();
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_concat() {
let test1 =
"(concat (concat (concat (concat u\"\\u{1F926}\" u\"\\u{1F3FC}\") u\"\\u{200D}\") u\"\\u{2642}\") u\"\\u{FE0F}\")";
let expected = Value::string_utf8_from_bytes("🤦🏼‍♂️".into()).unwrap();
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_get_len() {
let test1 = "(len \"ABCDE\")";
let expected = Value::UInt(5);
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_utf8_get_len() {
let test1 = "(len u\"ABCDE\\u{1F926}\\u{1F3FC}\\u{200D}\\u{2642}\\u{FE0F}\")";
let expected = Value::UInt(10);
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_string_ascii_max_len() {
let tests = [
"(as-max-len? \"ABC\" u3)",
"(as-max-len? \"ABC\" u2)",
"(as-max-len? \"ABC\" u4)",
];
let expected = [
Value::some(Value::string_ascii_from_bytes("ABC".into()).unwrap()).unwrap(),
Value::none(),
Value::some(Value::string_ascii_from_bytes("ABC".into()).unwrap()).unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute(test).unwrap().unwrap());
}
}
#[test]
fn test_string_utf8_max_len() {
let tests = [
"(as-max-len? u\"ABCDE\\u{1F926}\\u{1F3FC}\\u{200D}\\u{2642}\\u{FE0F}\" u10)",
"(as-max-len? u\"ABCDE\\u{1F926}\\u{1F3FC}\\u{200D}\\u{2642}\\u{FE0F}\" u9)",
"(as-max-len? u\"ABCDE\\u{1F926}\\u{1F3FC}\\u{200D}\\u{2642}\\u{FE0F}\" u11)"];
let expected = [
Value::some(Value::string_utf8_from_bytes("ABCDE🤦🏼".into()).unwrap()).unwrap(),
Value::none(),
Value::some(Value::string_utf8_from_bytes("ABCDE🤦🏼".into()).unwrap()).unwrap(),
];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute(test).unwrap().unwrap());
}
}
#[test]
fn test_simple_map_list() {
let test1 =
@@ -138,36 +309,36 @@ fn test_simple_list_concat() {
#[test]
fn test_simple_buff_concat() {
let tests = [
"(concat \"012\" \"34\")",
"(concat \"\" \"\")",
"(concat \"\" \"1\")",
"(concat \"1\" \"\")"];
"(concat 0x303132 0x3334)",
"(concat 0x00 0x00)",
"(concat 0x00 0x31)",
"(concat 0x31 0x00)"];
let expected = [
Value::buff_from(vec![48, 49, 50, 51, 52]).unwrap(),
Value::buff_from(vec![]).unwrap(),
Value::buff_from(vec![49]).unwrap(),
Value::buff_from(vec![49]).unwrap()];
Value::buff_from(vec![0, 0]).unwrap(),
Value::buff_from(vec![0, 49]).unwrap(),
Value::buff_from(vec![49, 0]).unwrap()];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute(test).unwrap().unwrap());
}
assert_eq!(
execute("(concat \"1\" 3)").unwrap_err(),
execute("(concat 0x31 3)").unwrap_err(),
RuntimeErrorType::BadTypeConstruction.into());
assert_eq!(
execute("(concat \"1\" (list 1))").unwrap_err(),
execute("(concat 0x31 (list 1))").unwrap_err(),
RuntimeErrorType::BadTypeConstruction.into());
}
#[test]
fn test_simple_buff_assert_max_len() {
let tests = [
"(as-max-len? \"123\" u3)",
"(as-max-len? \"123\" u2)",
"(as-max-len? \"123\" u5)"];
"(as-max-len? 0x313233 u3)",
"(as-max-len? 0x313233 u2)",
"(as-max-len? 0x313233 u5)"];
let expected = [
Value::some(Value::buff_from(vec![49, 50, 51]).unwrap()).unwrap(),
@@ -179,20 +350,20 @@ fn test_simple_buff_assert_max_len() {
}
assert_eq!(
execute("(as-max-len? \"123\")").unwrap_err(),
execute("(as-max-len? 0x313233)").unwrap_err(),
CheckErrors::IncorrectArgumentCount(2, 1).into());
assert_eq!(
execute("(as-max-len? \"123\" 3)").unwrap_err(),
execute("(as-max-len? 0x313233 3)").unwrap_err(),
CheckErrors::TypeError(UIntType, IntType).into());
assert_eq!(
execute("(as-max-len? 1 u3)").unwrap_err(),
CheckErrors::ExpectedListOrBuffer(IntType).into());
CheckErrors::ExpectedSequence(IntType).into());
assert_eq!(
execute("(as-max-len? \"123\" \"1\")").unwrap_err(),
CheckErrors::TypeError(UIntType, BufferType(1_u32.try_into().unwrap())).into());
execute("(as-max-len? 0x313233 0x31)").unwrap_err(),
CheckErrors::TypeError(UIntType, SequenceType(SequenceSubtype::BufferType(1_u32.try_into().unwrap()))).into());
}
#[test]
@@ -215,8 +386,8 @@ fn test_simple_list_assert_max_len() {
#[test]
fn test_simple_map_buffer() {
let test1 =
"(define-private (incr (x (buff 1))) \"1\")
(map incr \"0000\")";
"(define-private (incr (x (buff 1))) 0x31)
(map incr 0x30303030)";
let expected = Value::list_from(vec![
Value::buff_from(vec![49]).unwrap(),
@@ -226,18 +397,17 @@ fn test_simple_map_buffer() {
assert_eq!(expected, execute(test1).unwrap().unwrap());
}
#[test]
fn test_simple_filter_list() {
let test1 = "(define-private (test (x int)) (is-eq 0 (mod x 2)))
(filter test (list 1 2 3 4 5))";
let bad_tests = [
"(filter 123 (list 123))", // must have function name supplied
"(filter 123 (list 123))", // must have function name supplied
"(filter not (list 123) 3)", // must be 2 args
"(filter +)", // must be 2 args
"(filter not false)", // must supply list
"(filter - (list 1 2 3))"]; // must return bool
"(filter +)", // must be 2 args
"(filter not false)", // must supply list
"(filter - (list 1 2 3))"]; // must return bool
let expected = Value::list_from(vec![
@@ -253,8 +423,8 @@ fn test_simple_filter_list() {
#[test]
fn test_simple_filter_buffer() {
let test1 = "(define-private (test (x (buff 1))) (not (is-eq x \"0\")))
(filter test \"000123\")";
let test1 = "(define-private (test (x (buff 1))) (not (is-eq x 0x30)))
(filter test 0x303030313233)";
let expected = Value::buff_from(vec![49, 50, 51]).unwrap();
assert_eq!(expected, execute(test1).unwrap().unwrap());
@@ -263,28 +433,27 @@ fn test_simple_filter_buffer() {
#[test]
fn test_list_tuple_admission() {
let test =
"(define-private (bufferize (x int)) (if (is-eq x 1) \"abc\" \"ab\"))
"(define-private (bufferize (x int)) (if (is-eq x 1) 0x616263 0x6162))
(define-private (tuplize (x int))
(tuple (value (bufferize x))))
(map tuplize (list 0 1 0 1 0 1))";
let expected_type =
"(list (tuple (value \"012\"))
(tuple (value \"012\"))
(tuple (value \"012\"))
(tuple (value \"012\"))
(tuple (value \"012\"))
(tuple (value \"012\")))";
"(list (tuple (value 0x303132))
(tuple (value 0x303132))
(tuple (value 0x303132))
(tuple (value 0x303132))
(tuple (value 0x303132))
(tuple (value 0x303132)))";
let not_expected_type =
"(list (tuple (value \"01\"))
(tuple (value \"02\"))
(tuple (value \"12\"))
(tuple (value \"12\"))
(tuple (value \"01\"))
(tuple (value \"02\")))";
"(list (tuple (value 0x3031))
(tuple (value 0x3032))
(tuple (value 0x3132))
(tuple (value 0x3132))
(tuple (value 0x3031))
(tuple (value 0x3032)))";
let result_type = TypeSignature::type_of(&execute(test).unwrap().unwrap());
let expected_type = TypeSignature::type_of(&execute(expected_type).unwrap().unwrap());
let testing_value = &execute(not_expected_type).unwrap().unwrap();
@@ -307,11 +476,11 @@ fn test_simple_folds_list() {
}
#[test]
fn test_simple_folds_buffer() {
fn test_simple_folds_string() {
let tests =
["(define-private (get-len (x (buff 1)) (acc int)) (+ acc 1))
["(define-private (get-len (x (string-ascii 1)) (acc int)) (+ acc 1))
(fold get-len \"blockstack\" 0)",
"(define-private (slice (x (buff 1)) (acc (tuple (limit uint) (cursor uint) (data (buff 10)))))
"(define-private (slice (x (string-ascii 1)) (acc (tuple (limit uint) (cursor uint) (data (string-ascii 10)))))
(if (< (get cursor acc) (get limit acc))
(let ((data (default-to (get data acc) (as-max-len? (concat (get data acc) x) u10))))
(tuple (limit (get limit acc)) (cursor (+ u1 (get cursor acc))) (data data)))
@@ -320,7 +489,7 @@ fn test_simple_folds_buffer() {
let expected = [
Value::Int(10),
Value::buff_from(vec![48, 49, 50, 51, 52]).unwrap()];
Value::string_ascii_from_bytes(vec![48, 49, 50, 51, 52]).unwrap()];
for (test, expected) in tests.iter().zip(expected.iter()) {
assert_eq!(expected.clone(), execute(test).unwrap().unwrap());

View File

@@ -2,6 +2,7 @@ use vm::{eval, execute as vm_execute};
use vm::database::MemoryBackingStore;
use vm::errors::{CheckErrors, ShortReturnType, RuntimeErrorType, Error};
use vm::{Value, LocalContext, ContractContext, GlobalContext, Environment, CallStack};
use vm::types::{SequenceData};
use vm::contexts::{OwnedEnvironment};
use vm::callables::DefinedFunction;
use vm::types::{TypeSignature, BuffData, QualifiedContractIdentifier};
@@ -55,13 +56,13 @@ fn test_simple_let() {
#[test]
fn test_sha256() {
let sha256_evals = [
"(sha256 \"\")",
"(sha256 0x)",
"(sha256 0)",
"(sha256 \"The quick brown fox jumps over the lazy dog\")",
"(sha256 0x54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67)", // The quick brown fox jumps over the lazy dog
];
fn to_buffer(hex: &str) -> Value {
return Value::Buffer(BuffData { data: hex_bytes(hex).unwrap() });
return Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes(hex).unwrap() }));
}
let expectations = [
@@ -77,14 +78,14 @@ fn test_sha256() {
#[test]
fn test_sha512() {
let sha512_evals = [
"(sha512 \"\")",
"(sha512 0x)",
"(sha512 0)",
"(sha512 \"The quick brown fox jumps over the lazy dog\")",
"(sha512 0x54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67)", // The quick brown fox jumps over the lazy dog
];
fn p_to_hex(val: Value) -> String {
match val {
Value::Buffer(BuffData { data }) => to_hex(&data),
Value::Sequence(SequenceData::Buffer(BuffData { data })) => to_hex(&data),
_ => panic!("Failed")
}
}
@@ -102,14 +103,14 @@ fn test_sha512() {
#[test]
fn test_sha512trunc256() {
let sha512_evals = [
"(sha512/256 \"\")",
"(sha512/256 0x)",
"(sha512/256 0)",
"(sha512/256 \"The quick brown fox jumps over the lazy dog\")",
"(sha512/256 0x54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67)", // The quick brown fox jumps over the lazy dog
];
fn p_to_hex(val: Value) -> String {
match val {
Value::Buffer(BuffData { data }) => to_hex(&data),
Value::Sequence(SequenceData::Buffer(BuffData { data })) => to_hex(&data),
_ => panic!("Failed")
}
}
@@ -127,13 +128,13 @@ fn test_sha512trunc256() {
#[test]
fn test_keccak256() {
let keccak256_evals = [
"(keccak256 \"\")",
"(keccak256 0x)",
"(keccak256 0)",
"(keccak256 \"The quick brown fox jumps over the lazy dog\")",
"(keccak256 0x54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67)", // The quick brown fox jumps over the lazy dog
];
fn to_buffer(hex: &str) -> Value {
return Value::Buffer(BuffData { data: hex_bytes(hex).unwrap() });
return Value::Sequence(SequenceData::Buffer(BuffData { data: hex_bytes(hex).unwrap() }));
}
let expectations = [
@@ -265,7 +266,8 @@ fn test_concat_append_supertype() {
];
tests.iter().zip(expectations.iter())
.for_each(|(program, expectation)| assert_eq!(expectation.clone(), execute(program)));
.for_each(|(program, expectation)| {
assert_eq!(expectation.clone(), execute(program))});
}
#[test]

View File

@@ -1,6 +1,4 @@
use vm::types::{Value, TypeSignature, QualifiedContractIdentifier, ResponseData, PrincipalData};
use vm::types::TypeSignature::{IntType, UIntType, BoolType, ListType, BufferType};
use vm::types::signatures::{ListTypeData};
use vm::contexts::{OwnedEnvironment,GlobalContext, Environment};
use vm::execute as vm_execute;
use vm::errors::{CheckErrors, RuntimeErrorType, Error};

View File

@@ -4,6 +4,9 @@ pub mod signatures;
use std::{fmt, cmp};
use std::convert::{TryInto, TryFrom};
use std::collections::BTreeMap;
use std::{char, str};
use regex::Regex;
use address::c32;
use vm::representations::{ClarityName, ContractName, SymbolicExpression, SymbolicExpressionType};
@@ -12,8 +15,8 @@ use util::hash;
pub use vm::types::signatures::{
TupleTypeSignature, AssetIdentifier, FixedFunction, FunctionSignature,
TypeSignature, FunctionType, ListTypeData, FunctionArg, parse_name_type_pairs,
BUFF_64, BUFF_32, BUFF_20, BufferLength
TypeSignature, SequenceSubtype, StringSubtype, FunctionType, ListTypeData, FunctionArg, parse_name_type_pairs,
BUFF_64, BUFF_32, BUFF_20, BUFF_1, BufferLength, StringUTF8Length
};
pub const MAX_VALUE_SIZE: u32 = 1024 * 1024; // 1MB
@@ -174,14 +177,257 @@ pub enum Value {
Int(i128),
UInt(u128),
Bool(bool),
Buffer(BuffData),
List(ListData),
Sequence(SequenceData),
Principal(PrincipalData),
Tuple(TupleData),
Optional(OptionalData),
Response(ResponseData),
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum SequenceData {
Buffer(BuffData),
List(ListData),
String(CharType),
}
impl SequenceData {
pub fn atom_values(&mut self) -> Vec<SymbolicExpression> {
match self {
SequenceData::Buffer(ref mut data) => data.atom_values(),
SequenceData::List(ref mut data) => data.atom_values(),
SequenceData::String(CharType::ASCII(ref mut data)) => data.atom_values(),
SequenceData::String(CharType::UTF8(ref mut data)) => data.atom_values(),
}
}
pub fn len(&self) -> usize {
match &self {
SequenceData::Buffer(data) => data.items().len(),
SequenceData::List(data) => data.items().len(),
SequenceData::String(CharType::ASCII(data)) => data.items().len(),
SequenceData::String(CharType::UTF8(data)) => data.items().len(),
}
}
pub fn filter<F>(&mut self, filter: &mut F) -> Result<()> where F: FnMut(SymbolicExpression) -> Result<bool> {
// Note: this macro can probably get removed once
// ```Vec::drain_filter<F>(&mut self, filter: F) -> DrainFilter<T, F>```
// is available in rust stable channel (experimental at this point).
macro_rules! drain_filter {
($data:expr, $seq_type:ident) => {
let mut i = 0;
while i != $data.data.len() {
let atom_value = SymbolicExpression::atom_value($seq_type::to_value(&$data.data[i]));
match filter(atom_value) {
Ok(res) if res == false => { $data.data.remove(i); },
Ok(_) => { i += 1; },
Err(err) => return Err(err),
}
}
};
}
match self {
SequenceData::Buffer(ref mut data) => {
drain_filter!(data, BuffData);
},
SequenceData::List(ref mut data) => {
drain_filter!(data, ListData);
},
SequenceData::String(CharType::ASCII(ref mut data)) => {
drain_filter!(data, ASCIIData);
},
SequenceData::String(CharType::UTF8(ref mut data)) => {
drain_filter!(data, UTF8Data);
},
}
Ok(())
}
pub fn append(&mut self, other_seq: &mut SequenceData) -> Result<()> {
match (self, other_seq) {
(SequenceData::List(ref mut inner_data), SequenceData::List(ref mut other_inner_data)) => {
inner_data.append(other_inner_data)
},
(SequenceData::Buffer(ref mut inner_data), SequenceData::Buffer(ref mut other_inner_data)) => {
inner_data.append(other_inner_data)
},
(SequenceData::String(CharType::ASCII(ref mut inner_data)), SequenceData::String(CharType::ASCII(ref mut other_inner_data))) => {
inner_data.append(other_inner_data)
},
(SequenceData::String(CharType::UTF8(ref mut inner_data)), SequenceData::String(CharType::UTF8(ref mut other_inner_data))) => {
inner_data.append(other_inner_data)
},
_ => Err(RuntimeErrorType::BadTypeConstruction.into())
}?;
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum CharType {
UTF8(UTF8Data),
ASCII(ASCIIData),
}
impl fmt::Display for CharType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CharType::ASCII(string) => write!(f, "{}", string),
CharType::UTF8(string) => write!(f, "{}", string),
}
}
}
impl fmt::Debug for CharType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ASCIIData {
pub data: Vec<u8>,
}
impl fmt::Display for ASCIIData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut escaped_str = String::new();
for c in self.data.iter() {
let escaped_char = format!("{}", std::ascii::escape_default(*c));
escaped_str.push_str(&escaped_char);
}
write!(f, "{}", format!("\"{}\"", escaped_str))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UTF8Data {
pub data: Vec<Vec<u8>>,
}
impl fmt::Display for UTF8Data {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut result = String::new();
for c in self.data.iter() {
if c.len() > 1 {
// We escape extended charset
result.push_str(&format!("\\u{{{}}}", hash::to_hex(&c[..])));
} else {
// We render an ASCII char, escaped
let escaped_char = format!("{}", std::ascii::escape_default(c[0]));
result.push_str(&escaped_char);
}
}
write!(f, "{}", format!("u\"{}\"", result))
}
}
pub trait SequencedValue<T> {
fn type_signature(&self) -> TypeSignature;
fn items(&self) -> &Vec<T>;
fn drained_items(&mut self) -> Vec<T>;
fn to_value(v: &T) -> Value;
fn atom_values(&mut self) -> Vec<SymbolicExpression> {
self.drained_items().iter().map(|item| {
SymbolicExpression::atom_value(Self::to_value(&item))
}).collect()
}
}
impl SequencedValue<Value> for ListData {
fn items(&self) -> &Vec<Value> {
&self.data
}
fn drained_items(&mut self) -> Vec<Value> {
self.data.drain(..).collect()
}
fn type_signature(&self) -> TypeSignature {
TypeSignature::SequenceType(SequenceSubtype::ListType(self.type_signature.clone()))
}
fn to_value(v: &Value) -> Value {
v.clone()
}
}
impl SequencedValue<u8> for BuffData {
fn items(&self) -> &Vec<u8> {
&self.data
}
fn drained_items(&mut self) -> Vec<u8> {
self.data.drain(..).collect()
}
fn type_signature(&self) -> TypeSignature {
let buff_length = BufferLength::try_from(self.data.len())
.expect("ERROR: Too large of a buffer successfully constructed.");
TypeSignature::SequenceType(SequenceSubtype::BufferType(buff_length))
}
fn to_value(v: &u8) -> Value {
Value::buff_from_byte(*v)
}
}
impl SequencedValue<u8> for ASCIIData {
fn items(&self) -> &Vec<u8> {
&self.data
}
fn drained_items(&mut self) -> Vec<u8> {
self.data.drain(..).collect()
}
fn type_signature(&self) -> TypeSignature {
let buff_length = BufferLength::try_from(self.data.len())
.expect("ERROR: Too large of a buffer successfully constructed.");
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(buff_length)))
}
fn to_value(v: &u8) -> Value {
Value::string_ascii_from_bytes(vec![*v])
.expect("ERROR: Invalid ASCII string successfully constructed")
}
}
impl SequencedValue<Vec<u8>> for UTF8Data {
fn items(&self) -> &Vec<Vec<u8>> {
&self.data
}
fn drained_items(&mut self) -> Vec<Vec<u8>> {
self.data.drain(..).collect()
}
fn type_signature(&self) -> TypeSignature {
let str_len = StringUTF8Length::try_from(self.data.len())
.expect("ERROR: Too large of a buffer successfully constructed.");
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(str_len)))
}
fn to_value(v: &Vec<u8>) -> Value {
Value::string_utf8_from_bytes(v.clone())
.expect("ERROR: Invalid UTF8 string successfully constructed")
}
}
define_named_enum!(BlockInfoProperty {
Time("time"),
VrfSeed("vrf-seed"),
@@ -319,7 +565,7 @@ impl Value {
}
}
Ok(Value::List(ListData { data: list_data, type_signature: expected_type }))
Ok(Value::Sequence(SequenceData::List(ListData { data: list_data, type_signature: expected_type })))
}
pub fn list_from(list_data: Vec<Value>) -> Result<Value> {
@@ -330,18 +576,81 @@ impl Value {
// this is a problem _if_ the static analyzer cannot already prevent
// this case. This applies to all the constructor size checks.
let type_sig = TypeSignature::construct_parent_list_type(&list_data)?;
Ok(Value::List(ListData { data: list_data, type_signature: type_sig }))
Ok(Value::Sequence(SequenceData::List(ListData { data: list_data, type_signature: type_sig })))
}
pub fn buff_from(buff_data: Vec<u8>) -> Result<Value> {
// check the buffer size
BufferLength::try_from(buff_data.len())?;
// construct the buffer
Ok(Value::Buffer(BuffData { data: buff_data }))
Ok(Value::Sequence(SequenceData::Buffer(BuffData { data: buff_data })))
}
pub fn buff_from_byte(byte: u8) -> Value {
Value::Buffer(BuffData { data: vec![byte] })
Value::Sequence(SequenceData::Buffer(BuffData { data: vec![byte] }))
}
pub fn string_ascii_from_bytes(bytes: Vec<u8>) -> Result<Value> {
// check the string size
BufferLength::try_from(bytes.len())?;
for b in bytes.iter() {
if !b.is_ascii_alphanumeric() && !b.is_ascii_punctuation() && !b.is_ascii_whitespace() {
return Err(CheckErrors::InvalidCharactersDetected.into());
}
}
// construct the string
Ok(Value::Sequence(SequenceData::String(CharType::ASCII(ASCIIData { data: bytes }))))
}
pub fn string_utf8_from_string_utf8_literal(tokenized_str: String) -> Result<Value> {
let wrapped_codepoints_matcher = Regex::new("^\\\\u\\{(?P<value>[[:xdigit:]]+)\\}").unwrap();
let mut window = tokenized_str.as_str();
let mut cursor = 0;
let mut data: Vec<Vec<u8>> = vec![];
while !window.is_empty() {
if let Some(captures) = wrapped_codepoints_matcher.captures(window) {
let matched = captures.name("value").unwrap();
let scalar_value = window[matched.start()..matched.end()].to_string();
let unicode_char = {
let u = u32::from_str_radix(&scalar_value, 16).unwrap();
let c = char::from_u32(u).unwrap();
let mut encoded_char: Vec<u8> = vec![0; c.len_utf8()];
c.encode_utf8(&mut encoded_char[..]);
encoded_char
};
data.push(unicode_char);
cursor += scalar_value.len() + 4;
} else {
let ascii_char = window[0..1].to_string().into_bytes();
data.push(ascii_char);
cursor += 1;
}
// check the string size
StringUTF8Length::try_from(data.len())?;
window = &tokenized_str[cursor..];
}
// construct the string
Ok(Value::Sequence(SequenceData::String(CharType::UTF8(UTF8Data { data }))))
}
pub fn string_utf8_from_bytes(bytes: Vec<u8>) -> Result<Value> {
let validated_utf8_str = match str::from_utf8(&bytes) {
Ok(string) => string,
_ => return Err(CheckErrors::InvalidCharactersDetected.into())
};
let mut data = vec![];
for char in validated_utf8_str.chars() {
let mut encoded_char: Vec<u8> = vec![0; char.len_utf8()];
char.encode_utf8(&mut encoded_char[..]);
data.push(encoded_char);
}
// check the string size
StringUTF8Length::try_from(data.len())?;
Ok(Value::Sequence(SequenceData::String(CharType::UTF8(UTF8Data { data }))))
}
}
@@ -349,12 +658,49 @@ impl BuffData {
pub fn len(&self) -> BufferLength {
self.data.len().try_into().unwrap()
}
fn append(&mut self, other_seq: &mut BuffData) -> Result<()> {
self.data.append(&mut other_seq.data);
Ok(())
}
}
impl ListData {
pub fn len(&self) -> u32 {
self.data.len().try_into().unwrap()
}
fn append(&mut self, other_seq: &mut ListData) -> Result<()> {
let entry_type_a = self.type_signature.get_list_item_type();
let entry_type_b = other_seq.type_signature.get_list_item_type();
let entry_type = TypeSignature::factor_out_no_type(&entry_type_a, &entry_type_b)?;
let max_len = self.type_signature.get_max_len() + other_seq.type_signature.get_max_len();
self.type_signature = ListTypeData::new_list(entry_type, max_len)?;
self.data.append(&mut other_seq.data);
Ok(())
}
}
impl ASCIIData {
fn append(&mut self, other_seq: &mut ASCIIData) -> Result<()> {
self.data.append(&mut other_seq.data);
Ok(())
}
pub fn len(&self) -> BufferLength {
self.data.len().try_into().unwrap()
}
}
impl UTF8Data {
fn append(&mut self, other_seq: &mut UTF8Data) -> Result<()> {
self.data.append(&mut other_seq.data);
Ok(())
}
pub fn len(&self) -> BufferLength {
self.data.len().try_into().unwrap()
}
}
impl fmt::Display for OptionalData {
@@ -393,12 +739,13 @@ impl fmt::Display for Value {
Value::Int(int) => write!(f, "{}", int),
Value::UInt(int) => write!(f, "u{}", int),
Value::Bool(boolean) => write!(f, "{}", boolean),
Value::Buffer(vec_bytes) => write!(f, "0x{}", &vec_bytes),
Value::Tuple(data) => write!(f, "{}", data),
Value::Principal(principal_data) => write!(f, "{}", principal_data),
Value::Optional(opt_data) => write!(f, "{}", opt_data),
Value::Response(res_data) => write!(f, "{}", res_data),
Value::List(list_data) => {
Value::Sequence(SequenceData::Buffer(vec_bytes)) => write!(f, "0x{}", &vec_bytes),
Value::Sequence(SequenceData::String(string)) => write!(f, "{}", string),
Value::Sequence(SequenceData::List(list_data)) => {
write!(f, "(")?;
for (ix, v) in list_data.data.iter().enumerate() {
if ix > 0 {

View File

@@ -1,7 +1,7 @@
use vm::errors::{RuntimeErrorType, InterpreterResult, InterpreterError,
IncomparableError, Error as ClarityError, CheckErrors};
use vm::types::{Value, StandardPrincipalData, OptionalData, PrincipalData, BufferLength, MAX_VALUE_SIZE,
BOUND_VALUE_SERIALIZATION_BYTES,
use vm::types::{Value, SequenceSubtype, StringSubtype, StandardPrincipalData, OptionalData, PrincipalData, BufferLength, StringUTF8Length, MAX_VALUE_SIZE,
BOUND_VALUE_SERIALIZATION_BYTES, SequenceData, CharType,
TypeSignature, TupleData, QualifiedContractIdentifier, ResponseData};
use vm::database::{ClaritySerializable, ClarityDeserializable};
use vm::representations::{ClarityName, ContractName, MAX_STRING_LEN};
@@ -15,7 +15,7 @@ use serde_json::{Value as JSONValue};
use util::hash::{hex_bytes, to_hex};
use util::retry::{BoundReader};
use std::{error, fmt};
use std::{error, fmt, str};
use std::io::{Write, Read};
@@ -88,7 +88,9 @@ define_u8_enum!(TypePrefix {
OptionalNone = 9,
OptionalSome = 10,
List = 11,
Tuple = 12
Tuple = 12,
StringASCII = 13,
StringUTF8 = 14
});
impl From<&PrincipalData> for TypePrefix {
@@ -104,11 +106,12 @@ impl From<&PrincipalData> for TypePrefix {
impl From<&Value> for TypePrefix {
fn from(v: &Value) -> TypePrefix {
use super::Value::*;
use super::SequenceData::*;
use super::CharType;
match v {
Int(_) => TypePrefix::Int,
UInt(_) => TypePrefix::UInt,
Buffer(_) => TypePrefix::Buffer,
Bool(value) => {
if *value {
TypePrefix::BoolTrue
@@ -126,8 +129,11 @@ impl From<&Value> for TypePrefix {
},
Optional(OptionalData{ data: None }) => TypePrefix::OptionalNone,
Optional(OptionalData{ data: Some(_) }) => TypePrefix::OptionalSome,
List(_) => TypePrefix::List,
Tuple(_) => TypePrefix::Tuple,
Sequence(Buffer(_)) => TypePrefix::Buffer,
Sequence(List(_)) => TypePrefix::List,
Sequence(String(CharType::ASCII(_))) => TypePrefix::StringASCII,
Sequence(String(CharType::UTF8(_))) => TypePrefix::StringUTF8,
}
}
}
@@ -286,7 +292,7 @@ impl Value {
if let Some(x) = expected_type {
let passed_test = match x {
TypeSignature::BufferType(expected_len) => {
TypeSignature::SequenceType(SequenceSubtype::BufferType(expected_len)) => {
u32::from(&buffer_len) <= u32::from(expected_len)
},
_ => false
@@ -378,7 +384,7 @@ impl Value {
let (list_type, entry_type) = match expected_type {
None => (None, None),
Some(TypeSignature::ListType(list_type)) => {
Some(TypeSignature::SequenceType(SequenceSubtype::ListType(list_type))) => {
if len > list_type.get_max_len() {
return Err(SerializationError::DeserializeExpected(
expected_type.unwrap().clone()))
@@ -447,23 +453,74 @@ impl Value {
.map_err(|_| "Illegal tuple type".into())
.map(Value::from)
}
}
},
TypePrefix::StringASCII => {
let mut buffer_len = [0; 4];
r.read_exact(&mut buffer_len)?;
let buffer_len = BufferLength::try_from(
u32::from_be_bytes(buffer_len))?;
if let Some(x) = expected_type {
let passed_test = match x {
TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(expected_len))) => {
u32::from(&buffer_len) <= u32::from(expected_len)
},
_ => false
};
if !passed_test {
return Err(SerializationError::DeserializeExpected(x.clone()))
}
}
let mut data = vec![0; u32::from(buffer_len) as usize];
r.read_exact(&mut data[..])?;
// can safely unwrap, because the string length was _already_ checked.
Ok(Value::string_ascii_from_bytes(data).unwrap())
},
TypePrefix::StringUTF8 => {
let mut total_len = [0; 4];
r.read_exact(&mut total_len)?;
let total_len = BufferLength::try_from(
u32::from_be_bytes(total_len))?;
let mut data: Vec<u8> = vec![0; u32::from(total_len) as usize];
r.read_exact(&mut data[..])?;
let value = Value::string_utf8_from_bytes(data)
.map_err(|_| "Illegal string_utf8 type".into());
if let Some(x) = expected_type {
let passed_test = match (x, &value) {
(TypeSignature::SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(expected_len))),
Ok(Value::Sequence(SequenceData::String(CharType::UTF8(utf8))))) => {
utf8.data.len() as u32 <= u32::from(expected_len)
},
_ => false
};
if !passed_test {
return Err(SerializationError::DeserializeExpected(x.clone()))
}
}
value
},
}
}
pub fn serialize_write<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
use super::Value::*;
use super::SequenceData::{self, *};
use super::CharType::*;
use super::PrincipalData::*;
w.write_all(&[TypePrefix::from(self) as u8])?;
match self {
Int(value) => w.write_all(&value.to_be_bytes())?,
UInt(value) => w.write_all(&value.to_be_bytes())?,
Buffer(value) => {
w.write_all(&(u32::from(value.len()).to_be_bytes()))?;
w.write_all(&value.data)?
}
Principal(Standard(data)) => {
data.serialize_write(w)?
},
@@ -481,12 +538,28 @@ impl Value {
Optional(OptionalData{ data: Some(value) }) => {
value.serialize_write(w)?;
},
List(data) => {
Sequence(List(data)) => {
w.write_all(&data.len().to_be_bytes())?;
for item in data.data.iter() {
item.serialize_write(w)?;
}
},
Sequence(Buffer(value)) => {
w.write_all(&(u32::from(value.len()).to_be_bytes()))?;
w.write_all(&value.data)?
},
Sequence(SequenceData::String(UTF8(value))) => {
let total_len: u32 = value.data.iter()
.fold(0u32, |len, c| len + c.len() as u32);
w.write_all(&(total_len.to_be_bytes()))?;
for bytes in value.data.iter() {
w.write_all(&bytes)?
}
},
Sequence(SequenceData::String(ASCII(value))) => {
w.write_all(&(u32::from(value.len()).to_be_bytes()))?;
w.write_all(&value.data)?
},
Tuple(data) => {
w.write_all(&u32::try_from(data.data_map.len())
.unwrap()
@@ -565,7 +638,7 @@ mod tests {
use vm::types::TypeSignature::{IntType, BoolType};
fn buff_type(size: u32) -> TypeSignature {
TypeSignature::BufferType(size.try_into().unwrap()).into()
TypeSignature::SequenceType(SequenceSubtype::BufferType(size.try_into().unwrap())).into()
}
@@ -717,7 +790,31 @@ mod tests {
test_bad_expectation(
Value::buff_from(vec![0,0xde,0xad,0xbe,0xef,0]).unwrap(),
TypeSignature::from("(buff 2)"));
}
#[test]
fn test_string_ascii() {
test_deser_ser(Value::string_ascii_from_bytes(vec![61, 62, 63, 64]).unwrap());
// fail because we expect a shorter string
test_bad_expectation(
Value::string_ascii_from_bytes(vec![61, 62, 63, 64]).unwrap(),
TypeSignature::from("(string-ascii 3)"));
}
#[test]
fn test_string_utf8() {
test_deser_ser(Value::string_utf8_from_bytes(vec![61, 62, 63, 64]).unwrap());
test_deser_ser(Value::string_utf8_from_bytes(vec![61, 62, 63, 240, 159, 164, 151]).unwrap());
// fail because we expect a shorter string
test_bad_expectation(
Value::string_utf8_from_bytes(vec![61, 62, 63, 64]).unwrap(),
TypeSignature::from("(string-utf8 3)"));
test_bad_expectation(
Value::string_utf8_from_bytes(vec![61, 62, 63, 240, 159, 164, 151]).unwrap(),
TypeSignature::from("(string-utf8 3)"));
}
#[test]

View File

@@ -6,7 +6,7 @@ use std::collections::{BTreeMap, HashMap};
use address::c32;
use vm::costs::{cost_functions, CostOverflowingMath};
use vm::types::{Value, MAX_VALUE_SIZE, MAX_TYPE_DEPTH, WRAPPER_VALUE_SIZE,
use vm::types::{Value, SequenceData, SequencedValue, CharType, MAX_VALUE_SIZE, MAX_TYPE_DEPTH, WRAPPER_VALUE_SIZE,
QualifiedContractIdentifier, StandardPrincipalData, TraitIdentifier};
use vm::representations::{SymbolicExpression, SymbolicExpressionType, ClarityName, ContractName, TraitDefinition};
use vm::errors::{RuntimeErrorType, CheckErrors, IncomparableError, Error as VMError};
@@ -44,6 +44,9 @@ pub struct TupleTypeSignature {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BufferLength (u32);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StringUTF8Length (u32);
// INVARIANTS enforced by the Type Signatures.
// 1. A TypeSignature constructor will always fail rather than construct a
// type signature for a too large or invalid type. This is why any variable length
@@ -57,32 +60,56 @@ pub enum TypeSignature {
IntType,
UIntType,
BoolType,
BufferType(BufferLength),
SequenceType(SequenceSubtype),
PrincipalType,
ListType(ListTypeData),
TupleType(TupleTypeSignature),
OptionalType(Box<TypeSignature>),
ResponseType(Box<(TypeSignature, TypeSignature)>),
TraitReferenceType(TraitIdentifier),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SequenceSubtype {
BufferType(BufferLength),
ListType(ListTypeData),
StringType(StringSubtype),
}
impl SequenceSubtype {
pub fn unit_type(&self) -> TypeSignature {
match &self {
SequenceSubtype::ListType(ref list_data) => list_data.clone().destruct().0,
SequenceSubtype::BufferType(_) => TypeSignature::min_buffer(),
SequenceSubtype::StringType(StringSubtype::ASCII(_)) => TypeSignature::min_string_ascii(),
SequenceSubtype::StringType(StringSubtype::UTF8(_)) => TypeSignature::min_string_utf8(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum StringSubtype {
ASCII(BufferLength),
UTF8(StringUTF8Length),
}
use self::TypeSignature::{
NoType,
IntType,
UIntType,
BoolType,
BufferType,
SequenceType,
PrincipalType,
ListType,
TupleType,
OptionalType,
ResponseType,
TraitReferenceType
};
pub const BUFF_64: TypeSignature = BufferType(BufferLength(64));
pub const BUFF_32: TypeSignature = BufferType(BufferLength(32));
pub const BUFF_20: TypeSignature = BufferType(BufferLength(20));
pub const BUFF_64: TypeSignature = SequenceType(SequenceSubtype::BufferType(BufferLength(64)));
pub const BUFF_32: TypeSignature = SequenceType(SequenceSubtype::BufferType(BufferLength(32)));
pub const BUFF_20: TypeSignature = SequenceType(SequenceSubtype::BufferType(BufferLength(20)));
pub const BUFF_1: TypeSignature = SequenceType(SequenceSubtype::BufferType(BufferLength(1)));
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListTypeData {
@@ -136,7 +163,7 @@ impl From<FixedFunction> for FunctionSignature {
impl From<ListTypeData> for TypeSignature {
fn from(data: ListTypeData) -> Self {
ListType(data)
SequenceType(SequenceSubtype::ListType(data))
}
}
@@ -185,12 +212,67 @@ impl TryFrom<i128> for BufferLength {
fn try_from(data: i128) -> Result<BufferLength> {
if data > (MAX_VALUE_SIZE as i128) {
Err(CheckErrors::ValueTooLarge)
} else if data < 0 {
Err(CheckErrors::ValueOutOfBounds)
} else {
Ok(BufferLength(data as u32))
}
}
}
impl From<&StringUTF8Length> for u32 {
fn from(v: &StringUTF8Length) -> u32 {
v.0
}
}
impl From<StringUTF8Length> for u32 {
fn from(v: StringUTF8Length) -> u32 {
v.0
}
}
impl TryFrom<u32> for StringUTF8Length {
type Error = CheckErrors;
fn try_from(data: u32) -> Result<StringUTF8Length> {
let len = data.checked_mul(4)
.ok_or_else(|| CheckErrors::ValueTooLarge)?;
if len > MAX_VALUE_SIZE {
Err(CheckErrors::ValueTooLarge)
} else {
Ok(StringUTF8Length(data))
}
}
}
impl TryFrom<usize> for StringUTF8Length {
type Error = CheckErrors;
fn try_from(data: usize) -> Result<StringUTF8Length> {
let len = data.checked_mul(4)
.ok_or_else(|| CheckErrors::ValueTooLarge)?;
if len > (MAX_VALUE_SIZE as usize) {
Err(CheckErrors::ValueTooLarge)
} else {
Ok(StringUTF8Length(data as u32))
}
}
}
impl TryFrom<i128> for StringUTF8Length {
type Error = CheckErrors;
fn try_from(data: i128) -> Result<StringUTF8Length> {
let len = data.checked_mul(4)
.ok_or_else(|| CheckErrors::ValueTooLarge)?;
if len > (MAX_VALUE_SIZE as i128) {
Err(CheckErrors::ValueTooLarge)
} else if data < 0 {
Err(CheckErrors::ValueOutOfBounds)
} else {
Ok(StringUTF8Length(data as u32))
}
}
}
impl ListTypeData {
pub fn new_list(entry_type: TypeSignature, max_len: u32) -> Result<ListTypeData> {
let would_be_depth = 1 + entry_type.depth();
@@ -277,8 +359,8 @@ impl TypeSignature {
pub fn admits_type(&self, other: &TypeSignature) -> bool {
match self {
ListType(ref my_list_type) => {
if let ListType(other_list_type) = other {
SequenceType(SequenceSubtype::ListType(ref my_list_type)) => {
if let SequenceType(SequenceSubtype::ListType(other_list_type)) = other {
if other_list_type.max_len <= 0 {
// if other is an empty list, a list type should always admit.
true
@@ -291,6 +373,27 @@ impl TypeSignature {
false
}
},
SequenceType(SequenceSubtype::BufferType(ref my_len)) => {
if let SequenceType(SequenceSubtype::BufferType(ref other_len)) = other {
my_len.0 >= other_len.0
} else {
false
}
},
SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(len))) => {
if let SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(other_len))) = other {
len.0 >= other_len.0
} else {
false
}
},
SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(len))) => {
if let SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(other_len))) = other {
len.0 >= other_len.0
} else {
false
}
},
OptionalType(ref my_inner_type) => {
if let OptionalType(other_inner_type) = other {
// Option types will always admit a "NoType" OptionalType -- which
@@ -323,13 +426,6 @@ impl TypeSignature {
false
}
},
BufferType(ref my_len) => {
if let BufferType(ref other_len) = other {
my_len.0 >= other_len.0
} else {
false
}
},
TupleType(ref tuple_sig) => {
if let TupleType(ref other_tuple_sig) = other {
tuple_sig.admits(other_tuple_sig)
@@ -479,20 +575,28 @@ impl FunctionArg {
impl TypeSignature {
pub fn empty_buffer() -> TypeSignature {
BufferType(0_u32.try_into().unwrap())
SequenceType(SequenceSubtype::BufferType(0_u32.try_into().unwrap()))
}
pub fn min_buffer() -> TypeSignature {
BufferType(1_u32.try_into().unwrap())
SequenceType(SequenceSubtype::BufferType(1_u32.try_into().unwrap()))
}
pub fn min_string_ascii() -> TypeSignature {
SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(1_u32.try_into().unwrap())))
}
pub fn min_string_utf8() -> TypeSignature {
SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(1_u32.try_into().unwrap())))
}
pub fn max_buffer() -> TypeSignature {
BufferType(BufferLength(u32::try_from(MAX_VALUE_SIZE)
.expect("FAIL: Max Clarity Value Size is no longer realizable in Buffer Type")))
SequenceType(SequenceSubtype::BufferType(BufferLength(u32::try_from(MAX_VALUE_SIZE)
.expect("FAIL: Max Clarity Value Size is no longer realizable in Buffer Type"))))
}
/// If one of the types is a NoType, return Ok(the other type), otherwise return least_supertype(a, b)
fn factor_out_no_type(a: &TypeSignature, b: &TypeSignature) -> Result<TypeSignature> {
pub fn factor_out_no_type(a: &TypeSignature, b: &TypeSignature) -> Result<TypeSignature> {
if a.is_no_type() {
Ok(b.clone())
} else if b.is_no_type() {
@@ -539,7 +643,7 @@ impl TypeSignature {
Ok(TupleTypeSignature::try_from(type_map_out).map(|x| x.into())
.expect("ERR: least_supertype attempted to construct a too-large supertype of two types"))
},
(ListType(ListTypeData{ max_len: len_a, entry_type: entry_a }), ListType(ListTypeData{ max_len: len_b, entry_type: entry_b })) => {
(SequenceType(SequenceSubtype::ListType(ListTypeData{ max_len: len_a, entry_type: entry_a })), SequenceType(SequenceSubtype::ListType(ListTypeData{ max_len: len_b, entry_type: entry_b }))) => {
let entry_type =
if *len_a == 0 {
*(entry_b.clone())
@@ -561,13 +665,29 @@ impl TypeSignature {
let some_type = Self::factor_out_no_type(some_a, some_b)?;
Ok(Self::new_option(some_type)?)
},
(BufferType(buff_a), BufferType(buff_b)) => {
(SequenceType(SequenceSubtype::BufferType(buff_a)), SequenceType(SequenceSubtype::BufferType(buff_b))) => {
let buff_len = if u32::from(buff_a) > u32::from(buff_b) {
buff_a
} else {
buff_b
}.clone();
Ok(BufferType(buff_len))
Ok(SequenceType(SequenceSubtype::BufferType(buff_len)))
},
(SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(string_a))), SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(string_b)))) => {
let str_len = if u32::from(string_a) > u32::from(string_b) {
string_a
} else {
string_b
}.clone();
Ok(SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(str_len))))
},
(SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(string_a))), SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(string_b)))) => {
let str_len = if u32::from(string_a) > u32::from(string_b) {
string_a
} else {
string_b
}.clone();
Ok(SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(str_len))))
},
(NoType, x) | (x, NoType) => {
Ok(x.clone())
@@ -599,14 +719,12 @@ impl TypeSignature {
Value::Int(_v) => IntType,
Value::UInt(_v) => UIntType,
Value::Bool(_v) => BoolType,
Value::Buffer(buff_data) => {
let buff_length = BufferLength::try_from(buff_data.data.len())
.expect("ERROR: Too large of a buffer successfully constructed.");
BufferType(buff_length)
},
Value::Tuple(v) => TupleType(
v.type_signature.clone()),
Value::List(list_data) => ListType(list_data.type_signature.clone()),
Value::Sequence(SequenceData::List(list_data)) => list_data.type_signature(),
Value::Sequence(SequenceData::Buffer(buff_data)) => buff_data.type_signature(),
Value::Sequence(SequenceData::String(CharType::ASCII(ascii_data))) => ascii_data.type_signature(),
Value::Sequence(SequenceData::String(CharType::UTF8(utf8_data))) => utf8_data.type_signature(),
Value::Optional(v) => v.type_signature(),
Value::Response(v) => v.type_signature()
}
@@ -680,7 +798,35 @@ impl TypeSignature {
}
if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr {
BufferLength::try_from(*buff_len)
.map(|buff_len| TypeSignature::BufferType(buff_len))
.map(|buff_len| SequenceType(SequenceSubtype::BufferType(buff_len)))
} else {
Err(CheckErrors::InvalidTypeDescription)
}
}
// Parses type signatures of the form:
// (string-utf8 10)
fn parse_string_utf8_type_repr(type_args: &[SymbolicExpression]) -> Result<TypeSignature> {
if type_args.len() != 1 {
return Err(CheckErrors::InvalidTypeDescription)
}
if let SymbolicExpressionType::LiteralValue(Value::Int(utf8_len)) = &type_args[0].expr {
StringUTF8Length::try_from(*utf8_len)
.map(|utf8_len| SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(utf8_len))))
} else {
Err(CheckErrors::InvalidTypeDescription)
}
}
// Parses type signatures of the form:
// (string-ascii 10)
fn parse_string_ascii_type_repr(type_args: &[SymbolicExpression]) -> Result<TypeSignature> {
if type_args.len() != 1 {
return Err(CheckErrors::InvalidTypeDescription)
}
if let SymbolicExpressionType::LiteralValue(Value::Int(buff_len)) = &type_args[0].expr {
BufferLength::try_from(*buff_len)
.map(|buff_len| SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(buff_len))))
} else {
Err(CheckErrors::InvalidTypeDescription)
}
@@ -719,6 +865,8 @@ impl TypeSignature {
match compound_type.as_ref() {
"list" => TypeSignature::parse_list_type_repr(rest, accounting),
"buff" => TypeSignature::parse_buff_type_repr(rest),
"string-utf8" => TypeSignature::parse_string_utf8_type_repr(rest),
"string-ascii" => TypeSignature::parse_string_ascii_type_repr(rest),
"tuple" => TypeSignature::parse_tuple_type_repr(rest, accounting),
"optional" => TypeSignature::parse_optional_type_repr(rest, accounting),
"response" => TypeSignature::parse_response_type_repr(rest, accounting),
@@ -791,11 +939,11 @@ impl TypeSignature {
match self {
// NoType's may be asked for their size at runtime --
// legal constructions like `(ok 1)` have NoType parts (if they have unknown error variant types).
TraitReferenceType(_) | NoType | IntType | UIntType | BoolType | PrincipalType | BufferType(_) => 1,
TraitReferenceType(_) | NoType | IntType | UIntType | BoolType | PrincipalType | SequenceType(SequenceSubtype::BufferType(_)) | SequenceType(SequenceSubtype::StringType(_))=> 1,
TupleType(tuple_sig) => {
1 + tuple_sig.max_depth()
},
ListType(list_type) => 1 + list_type.get_list_item_type().depth(),
SequenceType(SequenceSubtype::ListType(list_type)) => 1 + list_type.get_list_item_type().depth(),
OptionalType(t) => 1 + t.depth(),
ResponseType(v) => {
1 + cmp::max(v.0.depth(), v.1.depth())
@@ -817,9 +965,10 @@ impl TypeSignature {
UIntType => Some(16),
BoolType => Some(1),
PrincipalType => Some(148), // 20+128
BufferType(len) => Some(4 + u32::from(len)),
TupleType(tuple_sig) => tuple_sig.inner_size(),
ListType(list_type) => list_type.inner_size(),
SequenceType(SequenceSubtype::BufferType(len)) | SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(len))) => Some(4 + u32::from(len)),
SequenceType(SequenceSubtype::ListType(list_type)) => list_type.inner_size(),
SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(len))) => Some(4 + 4 * u32::from(len)),
OptionalType(t) => t.size().checked_add(WRAPPER_VALUE_SIZE),
ResponseType(v) => {
// ResponseTypes are 1 byte for the committed bool,
@@ -847,9 +996,11 @@ impl TypeSignature {
// These types all only use ~1 byte for their type enum
NoType | IntType | UIntType | BoolType | PrincipalType => Some(1),
// u32 length + type enum
BufferType(_) => Some(1 + 4),
TupleType(tuple_sig) => tuple_sig.type_size(),
ListType(list_type) => list_type.type_size(),
SequenceType(SequenceSubtype::BufferType(_)) => Some(1 + 4),
SequenceType(SequenceSubtype::ListType(list_type)) => list_type.type_size(),
SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(_))) => Some(1 + 4),
SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(_))) => Some(1 + 4),
OptionalType(t) => {
t.inner_type_size()?
.checked_add(1)
@@ -1010,12 +1161,14 @@ impl fmt::Display for TypeSignature {
IntType => write!(f, "int"),
UIntType => write!(f, "uint"),
BoolType => write!(f, "bool"),
BufferType(len) => write!(f, "(buff {})", len),
OptionalType(t) => write!(f, "(optional {})", t),
ResponseType(v) => write!(f, "(response {} {})", v.0, v.1),
TupleType(t) => write!(f, "{}", t),
PrincipalType => write!(f, "principal"),
ListType(list_type_data) => write!(f, "(list {} {})", list_type_data.max_len, list_type_data.entry_type),
SequenceType(SequenceSubtype::BufferType(len)) => write!(f, "(buff {})", len),
SequenceType(SequenceSubtype::ListType(list_type_data)) => write!(f, "(list {} {})", list_type_data.max_len, list_type_data.entry_type),
SequenceType(SequenceSubtype::StringType(StringSubtype::ASCII(len))) => write!(f, "(string-ascii {})", len),
SequenceType(SequenceSubtype::StringType(StringSubtype::UTF8(len))) => write!(f, "(string-utf8 {})", len),
TraitReferenceType(trait_alias) => write!(f, "<{}>", trait_alias.to_string()),
}
}
@@ -1027,6 +1180,12 @@ impl fmt::Display for BufferLength {
}
}
impl fmt::Display for StringUTF8Length {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for FunctionArg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.signature)
@@ -1049,7 +1208,7 @@ mod test {
#[test]
fn type_of_list_of_buffs() {
let value = execute("(list \"abc\" \"abcde\")").unwrap().unwrap();
let type_descr = "(list 2 (buff 5))".into();
let type_descr = "(list 2 (string-ascii 5))".into();
assert_eq!(TypeSignature::type_of(&value), type_descr);
}

View File

@@ -296,14 +296,14 @@ fn bitcoind_integration_test() {
1 => {
// On round 1, publish the KV contract
// $ cat /tmp/out.clar
// (define-map store ((key (buff 32))) ((value (buff 32))))
// (define-public (get-value (key (buff 32)))
// (define-map store ((key (string-ascii 32))) ((value (string-ascii 32))))
// (define-public (get-value (key (string-ascii 32)))
// (begin
// (print (concat "Getting key " key))
// (match (map-get? store ((key key)))
// entry (ok (get value entry))
// (err 0))))
// (define-public (set-value (key (buff 32)) (value (buff 32)))
// (define-public (set-value (key (string-ascii 32)) (value (string-ascii 32)))
// (begin
// (print (concat "Setting key " key))
// (map-set store ((key key)) ((value value)))
@@ -318,15 +318,15 @@ fn bitcoind_integration_test() {
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let header_hash = chain_tip.block.block_hash();
let consensus_hash = chain_tip.metadata.consensus_hash;
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe40000000000000001000000000000000001007f9308b891b1593029c520cae33c25f55c4e720f875c85f8845e0ee7204047a0223f3587c033e0ddb7b0618183c56bf27a1521adf433d71f17d86a7b90c72973030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100c90ae0235365f3a73c595f8c6ab3c529807feb3cb269247329c9a24218d50d3f34c7eef5d28ba26831affa652a73ec32f098fec4bf1decd1ceb3fde4b8ce216b030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
3 => {
// On round 3, publish a "set:foo=bar" transaction
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store set-value -e \"foo\" -e \"bar\"
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 2 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store set-value -e \"foo\" -e \"bar\"
let header_hash = chain_tip.block.block_hash();
let consensus_hash = chain_tip.metadata.consensus_hash;
let set_foo_bar = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000020000000000000000010132033d83ad5051a52cef15cb88a93ac046e91a7ea2c6bf2110efdf8827ad8e0c6d0fbce1087637647ecf771c16613637742c08a4422cddfe7af03227257061ad030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020200000003666f6f0200000003626172";
let set_foo_bar = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000020000000000000000010076df7ad6ddf5cf3d2eb5b96bed15c95bdb975470add5bedeee0b6f00e884c0213b6718ffd75fbb98783168bca19559798ac44647b330e481b19d3eba1b2248c6030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020d00000003666f6f0d00000003626172";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(set_foo_bar).unwrap().to_vec()).unwrap();
},
4 => {
@@ -334,7 +334,7 @@ fn bitcoind_integration_test() {
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 3 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let header_hash = chain_tip.block.block_hash();
let consensus_hash = chain_tip.metadata.consensus_hash;
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000300000000000000000100f1ffc472083f4fea947a6d1a83d0ddf0353dc0e9fac94d74da9d668b61676d1966474bc890f94c5fdb4d6ef816682f9073a2185e6ca8f8a6aa25a36ed851399d030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000300000000000000000101fd27e1727f78c38620dc155ca9940a02e964d08fcd35ac4fc8fbc56d62caac585891f537751626dc87fc7f212b3e7586845d36800e742c3f2b0c0a05cf81435e030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
5 => {

View File

@@ -25,14 +25,14 @@ use super::node::{TESTNET_CHAIN_ID};
use super::burnchains::bitcoin_regtest_controller::ParsedUTXO;
// $ cat /tmp/out.clar
pub const STORE_CONTRACT: &str = r#"(define-map store ((key (buff 32))) ((value (buff 32))))
(define-public (get-value (key (buff 32)))
pub const STORE_CONTRACT: &str = r#"(define-map store ((key (string-ascii 32))) ((value (string-ascii 32))))
(define-public (get-value (key (string-ascii 32)))
(begin
(print (concat "Getting key " key))
(match (map-get? store { key: key })
entry (ok (get value entry))
(err 0))))
(define-public (set-value (key (buff 32)) (value (buff 32)))
(define-public (set-value (key (string-ascii 32)) (value (string-ascii 32)))
(begin
(print (concat "Setting key " key))
(map-set store { key: key } { value: value })
@@ -215,19 +215,19 @@ fn should_succeed_mining_valid_txs() {
2 => {
// On round 2, publish a "get:foo" transaction
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe40000000000000001000000000000000001007f9308b891b1593029c520cae33c25f55c4e720f875c85f8845e0ee7204047a0223f3587c033e0ddb7b0618183c56bf27a1521adf433d71f17d86a7b90c72973030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100c90ae0235365f3a73c595f8c6ab3c529807feb3cb269247329c9a24218d50d3f34c7eef5d28ba26831affa652a73ec32f098fec4bf1decd1ceb3fde4b8ce216b030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
3 => {
// On round 3, publish a "set:foo=bar" transaction
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 2 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store set-value -e \"foo\" -e \"bar\"
let set_foo_bar = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000020000000000000000010132033d83ad5051a52cef15cb88a93ac046e91a7ea2c6bf2110efdf8827ad8e0c6d0fbce1087637647ecf771c16613637742c08a4422cddfe7af03227257061ad030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020200000003666f6f0200000003626172";
let set_foo_bar = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000020000000000000000010076df7ad6ddf5cf3d2eb5b96bed15c95bdb975470add5bedeee0b6f00e884c0213b6718ffd75fbb98783168bca19559798ac44647b330e481b19d3eba1b2248c6030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020d00000003666f6f0d00000003626172";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(set_foo_bar).unwrap().to_vec()).unwrap();
},
4 => {
// On round 4, publish a "get:foo" transaction
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 3 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000300000000000000000100f1ffc472083f4fea947a6d1a83d0ddf0353dc0e9fac94d74da9d668b61676d1966474bc890f94c5fdb4d6ef816682f9073a2185e6ca8f8a6aa25a36ed851399d030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000300000000000000000101fd27e1727f78c38620dc155ca9940a02e964d08fcd35ac4fc8fbc56d62caac585891f537751626dc87fc7f212b3e7586845d36800e742c3f2b0c0a05cf81435e030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
5 => {
@@ -343,7 +343,7 @@ fn should_succeed_mining_valid_txs() {
StacksTransactionEvent::SmartContractEvent(data) => {
format!("{}", data.key.0) == "STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A.store" &&
data.key.1 == "print" &&
format!("{}", data.value) == "0x53657474696e67206b657920666f6f" // "Setting key foo" in hexa
format!("{}", data.value) == "\"Setting key foo\"".to_string()
},
_ => false
});
@@ -379,7 +379,7 @@ fn should_succeed_mining_valid_txs() {
StacksTransactionEvent::SmartContractEvent(data) => {
format!("{}", data.key.0) == "STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A.store" &&
data.key.1 == "print" &&
format!("{}", data.value) == "0x47657474696e67206b657920666f6f" // "Getting key foo" in hexa
format!("{}", data.value) == "\"Getting key foo\"".to_string()
},
_ => false
});
@@ -451,20 +451,20 @@ fn should_succeed_handling_malformed_and_valid_txs() {
// On round 2, publish a "get:foo" transaction (mainnet instead of testnet).
// Will not be mined
// ./blockstack-cli contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let get_foo = "0000000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100cbb46766a2bc03261f6bd428fdd6ce63da8ed04713e6476426390ccc15d2b1c133d9ba30a47b51cd467a09a25f3d7fa2bb4b85379f7d0601df02268cb623e231030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "0000000001040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000101f5c408658708fca3aa625525b0d2314519af487f53e9a552eab6aeb01577dc5d6786eff505b5b781ed7b512bcbde871ab4d438394220080ca01a2a8c46b11361030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
3 => {
// On round 3, publish a "set:foo=bar" transaction (chain-id not matching).
// Will not be mined
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store set-value -e \"foo\" -e \"bar\"
let set_foo_bar = "8000000001040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000101e57846af212a3e9536c86446d3f39210f6edd691f5c6db65feea3e188822dc2c09e8f82b2f7449d54b58e1a6666b003f65c104f3f9b41a34211560b8ce2c1095030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020200000003666f6f0200000003626172";
let set_foo_bar = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe40000000000000001000000000000000001017112764d8a0c0a5476fc6ec37de6bc564259c6ccd4ef8ce06c1cd23f58c66a114485df6bbdf147ded8ae4fc6dda87686052bc9aa4734265c3ae4b64613b2ceb1030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265097365742d76616c7565000000020d00000003666f6f0d00000003626172";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(set_foo_bar).unwrap().to_vec()).unwrap();
},
4 => {
// On round 4, publish a "get:foo" transaction
// ./blockstack-cli --testnet contract-call 043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3 0 1 STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A store get-value -e \"foo\"
let get_foo = "8000000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100e11fa0938e579c868137cfdd95fc0d6107a32c7a8864bbff2852c792c1759a38314e42922702b709c7b17c93d406f9d8057fb7c14736e5d85ff24acf89e921d6030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010200000003666f6f";
let get_foo = "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100c90ae0235365f3a73c595f8c6ab3c529807feb3cb269247329c9a24218d50d3f34c7eef5d28ba26831affa652a73ec32f098fec4bf1decd1ceb3fde4b8ce216b030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f";
tenure.mem_pool.submit_raw(&consensus_hash, &header_hash,hex_bytes(get_foo).unwrap().to_vec()).unwrap();
},
_ => {}