mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-13 09:08:02 +08:00
Merge pull request #1779 from blockstack/feat/clarity-strings
Introduce string-ascii and string-utf8 (Clarity types)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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")
|
||||
},
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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)
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
177
src/vm/functions/sequences.rs
Normal file
177
src/vm/functions/sequences.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
@@ -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]
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
_ => {}
|
||||
|
||||
Reference in New Issue
Block a user