Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarity Value Serialization and Name String Guarding #1099

Merged
merged 21 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
12f0a8a
types.rs -> types/mod.rs
kantai Aug 19, 2019
f5f5961
Merge branch 'feature/general-cleanup' into feature/clarity-serializa…
kantai Aug 20, 2019
53a7bec
Merge remote-tracking branch 'origin/develop' into feature/clarity-se…
kantai Aug 20, 2019
0ac9a33
serializationclarity object serialization using our own impl of seria…
kantai Aug 21, 2019
2cc76f3
use expected types from the clarity_db to deserialize lists, tuples. …
kantai Aug 21, 2019
218be3f
refactor list typesignature, do type enforcement on value constructor…
kantai Aug 21, 2019
c5c2169
simple test for making sure get_value is setting max_len correctly on…
kantai Aug 21, 2019
ca1045b
serialization/deserialization code tests
kantai Aug 21, 2019
0e2a5f3
support deserializing with and without an expected_type.
kantai Aug 22, 2019
9d7425b
some light refactor, dead code removal from types module
kantai Aug 22, 2019
83db5da
more dead code removal, add test for contract-caller behavior
kantai Aug 22, 2019
3eaa70f
use our own hex serialization
kantai Aug 22, 2019
0d1e6a4
first steps toward guarded strings
kantai Aug 23, 2019
22dc81b
use guarded contractname type to represent contract names.
kantai Aug 25, 2019
21bc87a
use ClarityName in function args, ContractAnalysis
kantai Aug 26, 2019
32d50fe
a bit more restrictive regexes on the guarded strings
kantai Aug 26, 2019
36fdfc5
Added guarded strings to this branch. Merge branch 'feature/guarded-s…
kantai Aug 26, 2019
5742328
more dead code removal
kantai Aug 26, 2019
ce3b289
use ListTypeData::new() for list type construction. allow size of NoT…
kantai Aug 26, 2019
74257cd
only allow punction to start a name in native functions
kantai Sep 4, 2019
1297e42
use single & refs instead of double refs
kantai Sep 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/vm/analysis/build_contract_interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ impl ContractInterfaceAtomType {
TypeSignature::Atom(atom_type) => {
Self::from_atom_type(atom_type)
},
TypeSignature::List(atom_type, list_data) => {
TypeSignature::List(list_data) => {
ContractInterfaceAtomType::list {
type_f: Box::new(Self::from_atom_type(atom_type)),
type_f: Box::new(Self::from_atom_type(&list_data.atomic_type)),
length: list_data.max_len,
dimension: list_data.dimension
}
Expand Down
3 changes: 2 additions & 1 deletion src/vm/analysis/type_checker/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fn check_special_list_cons(checker: &mut TypeChecker, args: &[SymbolicExpression
};
CheckError::new(error_type)
})
.map(TypeSignature::from)
}

fn check_special_print(checker: &mut TypeChecker, args: &[SymbolicExpression], context: &TypingContext) -> TypeResult {
Expand Down Expand Up @@ -276,7 +277,7 @@ fn check_get_block_info(checker: &mut TypeChecker, args: &[SymbolicExpression],
let block_info_prop_str = args[0].match_atom()
.ok_or(CheckError::new(CheckErrors::GetBlockInfoExpectPropertyName))?;

let block_info_prop = BlockInfoProperty::from_str(block_info_prop_str)
let block_info_prop = BlockInfoProperty::lookup_by_name(block_info_prop_str)
.ok_or(CheckError::new(CheckErrors::NoSuchBlockInfoProperty(block_info_prop_str.to_string())))?;

checker.type_check_expects(&args[1], &context, &AtomTypeIdentifier::IntType.into())?;
Expand Down
10 changes: 0 additions & 10 deletions src/vm/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,4 @@ impl Contract {

Ok(Contract { contract_context: contract_context })
}

pub fn deserialize(json: &str) -> Contract {
serde_json::from_str(json)
.expect("Failed to deserialize contract")
}

pub fn serialize(&self) -> String {
serde_json::to_string(self)
.expect("Failed to serialize contract")
}
}
25 changes: 17 additions & 8 deletions src/vm/database/clarity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ impl <'a> ClarityDatabase <'a> {
.map(|x| T::deserialize(&x))
}

fn get_value (&mut self, key: &str, expected: &TypeSignature) -> Option<Value> {
self.store.get(&key)
.map(|json| Value::deserialize(&json, expected))
}

pub fn make_key_for_trip(contract_name: &str, data: StoreType, var_name: &str) -> String {
format!("vm::{}::{}::{}", contract_name, data as u8, var_name)
}
Expand Down Expand Up @@ -226,7 +231,7 @@ impl <'a> ClarityDatabase <'a> {

let key = ClarityDatabase::make_key_for_trip(contract_name, StoreType::Variable, variable_name);

let result = self.get(&key);
let result = self.get_value(&key, &variable_descriptor.value_type);

match result {
None => Ok(Value::none()),
Expand Down Expand Up @@ -267,7 +272,8 @@ impl <'a> ClarityDatabase <'a> {

let key = ClarityDatabase::make_key_for_quad(contract_name, StoreType::DataMap, map_name, key_value.serialize());

let result = self.get(&key);
let stored_type = TypeSignature::new_option(map_descriptor.value_type);
let result = self.get_value(&key, &stored_type);

match result {
None => Ok(Value::none()),
Expand All @@ -283,11 +289,11 @@ impl <'a> ClarityDatabase <'a> {
self.inner_set_entry(contract_name, map_name, key, value, true)
}

fn data_map_entry_exists(&mut self, key: &str) -> Result<bool> {
match self.store.get(&key) {
fn data_map_entry_exists(&mut self, key: &str, expected_value: &TypeSignature) -> Result<bool> {
match self.get_value(key, expected_value) {
None => Ok(false),
Some(serialized) =>
Ok(Value::deserialize(&serialized) != Value::none())
Some(value) =>
Ok(value != Value::none())
}
}

Expand All @@ -302,7 +308,9 @@ impl <'a> ClarityDatabase <'a> {

let key = ClarityDatabase::make_key_for_quad(contract_name, StoreType::DataMap, map_name, key_value.serialize());

if return_if_exists && self.data_map_entry_exists(&key)? {
let stored_type = TypeSignature::new_option(map_descriptor.value_type);

if return_if_exists && self.data_map_entry_exists(&key, &stored_type)? {
return Ok(Value::Bool(false))
}

Expand All @@ -318,7 +326,8 @@ impl <'a> ClarityDatabase <'a> {
}

let key = ClarityDatabase::make_key_for_quad(contract_name, StoreType::DataMap, map_name, key_value.serialize());
if !self.data_map_entry_exists(&key)? {
let stored_type = TypeSignature::new_option(map_descriptor.value_type);
if !self.data_map_entry_exists(&key, &stored_type)? {
return Ok(Value::Bool(false))
}

Expand Down
1 change: 0 additions & 1 deletion src/vm/database/structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ pub struct SimmedBlock {

clarity_serializable!(SimmedBlock);

clarity_serializable!(Value);
clarity_serializable!(PrincipalData);
clarity_serializable!(i128);
clarity_serializable!(u64);
Expand Down
16 changes: 14 additions & 2 deletions src/vm/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt;
use std::error;
use vm::types::Value;
use vm::types::{Value, TypeSignature};
use vm::contexts::StackTrace;
use chainstate::stacks::index::{Error as MarfError};

Expand Down Expand Up @@ -29,6 +29,7 @@ pub enum UncheckedError {
TypeError(String, Value),
InvalidArguments(String),
IncorrectArgumentCount(usize, usize),
ListLargerThanExpected,
UndefinedVariable(String),
UndefinedFunction(String),
UndefinedContract(String),
Expand Down Expand Up @@ -66,6 +67,10 @@ pub enum InterpreterError {
BadFileName,
FailedToCreateDataDirectory,
MarfFailure(IncomparableError<MarfError>),
DeserializeExpected(TypeSignature),
DeserializeUnexpectedTypeField(String),
FailureConstructingTupleWithType,
FailureConstructingListWithType
}


Expand All @@ -92,7 +97,8 @@ pub enum RuntimeErrorType {
NoSuchToken,
NotImplemented,
NoSenderInContext,
NonPositiveTokenSupply
NonPositiveTokenSupply,
JSONParseError(IncomparableError<SerdeJSONErr>),
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -147,6 +153,12 @@ impl error::Error for Error {
}
}

impl From<SerdeJSONErr> for Error {
fn from(err: SerdeJSONErr) -> Self {
Error::from(RuntimeErrorType::JSONParseError(IncomparableError { err }))
}
}

impl From<RuntimeErrorType> for Error {
fn from(err: RuntimeErrorType) -> Self {
Error::Runtime(err, None)
Expand Down
2 changes: 1 addition & 1 deletion src/vm/functions/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ pub fn special_get_block_info(args: &[SymbolicExpression],
let property_name = args[0].match_atom()
.ok_or(UncheckedError::ExpectedBlockPropertyName)?;

let block_info_prop = BlockInfoProperty::from_str(property_name)
let block_info_prop = BlockInfoProperty::lookup_by_name(property_name)
.ok_or(UncheckedError::ExpectedBlockPropertyName)?;

// Handle the block-height input arg clause.
Expand Down
4 changes: 2 additions & 2 deletions src/vm/functions/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use vm::representations::{SymbolicExpression, SymbolicExpressionType};
use vm::{LocalContext, Environment, eval, apply, lookup_function};

pub fn list_cons(args: &[Value]) -> Result<Value> {
Value::new_list(args)
Value::list_from(Vec::from(args))
}

pub fn list_filter(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
Expand All @@ -28,7 +28,7 @@ pub fn list_filter(args: &[SymbolicExpression], env: &mut Environment, context:
return Err(UncheckedError::TypeError("Bool".to_string(), filter_eval).into())
}
}
Value::list_from(output)
Value::list_with_type(output, list_data.type_signature)
} else {
Err(UncheckedError::TypeError("List".to_string(), list).into())
}
Expand Down
4 changes: 2 additions & 2 deletions src/vm/functions/tuples.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use vm::errors::{UncheckedError, InterpreterResult as Result, check_argument_count};
use vm::types::{Value};
use vm::types::{Value, TupleData};
use vm::representations::{SymbolicExpression,SymbolicExpressionType};
use vm::representations::SymbolicExpressionType::{List};
use vm::{LocalContext, Environment, eval};
Expand All @@ -19,7 +19,7 @@ pub fn tuple_cons(args: &[SymbolicExpression], env: &mut Environment, context: &

let bindings = parse_eval_bindings(args, env, context)?;

Value::tuple_from_data(bindings)
TupleData::from_data(bindings).map(Value::from)
}

pub fn tuple_get(args: &[SymbolicExpression], env: &mut Environment, context: &LocalContext) -> Result<Value> {
Expand Down
43 changes: 42 additions & 1 deletion src/vm/tests/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use vm::execute as vm_execute;
use vm::errors::{Error, UncheckedError};
use vm::types::{Value, StandardPrincipalData, ResponseData};
use vm::types::{Value, StandardPrincipalData, ResponseData, PrincipalData};
use vm::contexts::{OwnedEnvironment,GlobalContext, Environment};
use vm::representations::SymbolicExpression;
use vm::contracts::Contract;
Expand Down Expand Up @@ -198,6 +198,46 @@ fn test_simple_token_system(owned_env: &mut OwnedEnvironment) {
}
}

fn test_contract_caller(owned_env: &mut OwnedEnvironment) {
let contract_a =
"(define-read-only (get-caller)
(list contract-caller tx-sender))";
let contract_b =
"(define-read-only (get-caller)
(list contract-caller tx-sender))
(define-read-only (as-contract-get-caller)
(as-contract (get-caller)))
(define-read-only (cc-get-caller)
(contract-call! contract-a get-caller))
(define-read-only (as-contract-cc-get-caller)
(as-contract (contract-call! contract-a get-caller)))";

let p1 = execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR");

{
let mut env = owned_env.get_exec_environment(None);
env.initialize_contract("contract-a", contract_a).unwrap();
env.initialize_contract("contract-b", contract_b).unwrap();
}

{
let c_b = Value::from(PrincipalData::ContractPrincipal("contract-b".to_string()));
let mut env = owned_env.get_exec_environment(Some(p1.clone()));
assert_eq!(
env.execute_contract("contract-a", "get-caller", &vec![]).unwrap(),
Value::list_from(vec![p1.clone(), p1.clone()]).unwrap());
assert_eq!(
env.execute_contract("contract-b", "as-contract-get-caller", &vec![]).unwrap(),
Value::list_from(vec![c_b.clone(), c_b.clone()]).unwrap());
assert_eq!(
env.execute_contract("contract-b", "cc-get-caller", &vec![]).unwrap(),
Value::list_from(vec![c_b.clone(), p1.clone()]).unwrap());
assert_eq!(
env.execute_contract("contract-b", "as-contract-cc-get-caller", &vec![]).unwrap(),
Value::list_from(vec![c_b.clone(), c_b.clone()]).unwrap());
}
}

fn test_simple_naming_system(owned_env: &mut OwnedEnvironment) {
let tokens_contract = SIMPLE_TOKENS;

Expand Down Expand Up @@ -512,6 +552,7 @@ fn test_factorial_contract(owned_env: &mut OwnedEnvironment) {
fn test_all() {
let to_test = [ test_factorial_contract,
test_aborts,
test_contract_caller,
test_simple_naming_system,
test_simple_token_system,
test_simple_contract_call ];
Expand Down
28 changes: 26 additions & 2 deletions src/vm/tests/datamaps.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use vm::errors::{Error, UncheckedError, RuntimeErrorType};
use vm::types::{Value, StandardPrincipalData, TupleData};
use vm::types::{Value, StandardPrincipalData, TupleData, ListData};
use vm::contexts::{OwnedEnvironment};
use vm::execute;

Expand Down Expand Up @@ -296,10 +296,34 @@ fn test_set_list_variable() {
Value::list_from(vec![Value::Int(1), Value::Int(2), Value::Int(3)]).unwrap(),
Value::list_from(vec![Value::Int(2), Value::Int(3), Value::Int(1)]).unwrap(),
Value::list_from(vec![Value::Int(2), Value::Int(3), Value::Int(1)]).unwrap()
]);
]);
assert_executes(expected, &contract_src);
}

#[test]
fn test_get_list_max_len() {
let contract_src = r#"
(define-data-var ranking (list 10 int) (list 1 2 3))
(define-private (get-ranking)
(var-get ranking))
"#;

let mut contract_src = contract_src.to_string();
contract_src.push_str("(get-ranking)");

let actual_value = execute(&contract_src).unwrap().unwrap();

match actual_value {
Value::List(ListData { data, type_signature }) => {
assert_eq!(vec![Value::Int(1), Value::Int(2), Value::Int(3)],
data);
assert_eq!(type_signature.max_len, 10);
assert_eq!(type_signature.dimension, 1);
},
_ => panic!("Expected List")
};
}

#[test]
fn test_set_buffer_variable() {
let contract_src = r#"
Expand Down
Loading