Define ScriptPurpose & ScriptContext types in prelude, fix codegen new v3 wrapper.

This commit is contained in:
KtorZ 2024-08-14 18:52:50 +02:00
parent 03a348040b
commit 972e9bd763
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
4 changed files with 374 additions and 148 deletions

View File

@ -389,6 +389,53 @@ impl TypedDataType {
} }
} }
pub fn script_purpose() -> Self {
DataType {
constructors: vec![
RecordConstructor {
location: Span::empty(),
name: "__Spend".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
RecordConstructor {
location: Span::empty(),
name: "__Mint".to_string(),
arguments: vec![],
doc: None,
sugar: false,
},
],
doc: None,
location: Span::empty(),
name: "__ScriptPurpose".to_string(),
opaque: false,
parameters: vec![],
public: true,
typed_parameters: vec![],
}
}
pub fn script_context() -> Self {
DataType {
constructors: vec![RecordConstructor {
location: Span::empty(),
name: "__ScriptContext".to_string(),
arguments: vec![],
doc: None,
sugar: false,
}],
doc: None,
location: Span::empty(),
name: "__ScriptContext".to_string(),
opaque: false,
parameters: vec![],
public: true,
typed_parameters: vec![],
}
}
pub fn prng() -> Self { pub fn prng() -> Self {
DataType { DataType {
constructors: vec![ constructors: vec![

View File

@ -37,6 +37,16 @@ pub const REDEEMER_WRAPPER: &str = "RedeemerWrapper";
pub const PRNG: &str = "PRNG"; pub const PRNG: &str = "PRNG";
pub const FUZZER: &str = "Fuzzer"; pub const FUZZER: &str = "Fuzzer";
pub const SCRIPT_PURPOSE: &str = "__ScriptPurpose";
pub const SCRIPT_PURPOSE_SPEND: &str = "__Spend";
pub const SCRIPT_PURPOSE_MINT: &str = "__Mint";
pub const SCRIPT_PURPOSES_COUNT: u16 = 6;
pub const SCRIPT_CONTEXT: &str = "__ScriptContext";
pub const SCRIPT_CONTEXT_TRANSACTION: &str = "__Transaction";
pub const SCRIPT_CONTEXT_REDEEMER: &str = "__Redeemer";
pub const SCRIPT_CONTEXT_PURPOSE: &str = "__ScriptPurpose";
/// Build a prelude that can be injected /// Build a prelude that can be injected
/// into a compiler pipeline /// into a compiler pipeline
pub fn prelude(id_gen: &IdGenerator) -> TypeInfo { pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
@ -630,6 +640,77 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
}, },
); );
// Cardano ScriptContext
prelude.types_constructors.insert(
SCRIPT_CONTEXT.to_string(),
vec![
SCRIPT_CONTEXT_TRANSACTION.to_string(),
SCRIPT_CONTEXT_REDEEMER.to_string(),
SCRIPT_CONTEXT_PURPOSE.to_string(),
],
);
prelude.types.insert(
SCRIPT_CONTEXT.to_string(),
TypeConstructor {
location: Span::empty(),
public: true,
module: "".to_string(),
parameters: vec![],
tipo: script_context(),
},
);
// Cardano ScriptPurpose
prelude.types_constructors.insert(
SCRIPT_PURPOSE.to_string(),
vec![
SCRIPT_PURPOSE_SPEND.to_string(),
SCRIPT_PURPOSE_MINT.to_string(),
],
);
prelude.values.insert(
SCRIPT_PURPOSE_SPEND.to_string(),
ValueConstructor::public(
function(vec![data(), option(data())], script_purpose()),
ValueConstructorVariant::Record {
module: "".into(),
name: SCRIPT_PURPOSE_SPEND.to_string(),
field_map: None::<FieldMap>,
arity: 2,
location: Span::empty(),
constructors_count: SCRIPT_PURPOSES_COUNT,
},
),
);
prelude.values.insert(
SCRIPT_PURPOSE_MINT.to_string(),
ValueConstructor::public(
function(vec![data()], script_purpose()),
ValueConstructorVariant::Record {
module: "".into(),
name: SCRIPT_PURPOSE_MINT.to_string(),
field_map: None::<FieldMap>,
arity: 1,
location: Span::empty(),
constructors_count: SCRIPT_PURPOSES_COUNT,
},
),
);
prelude.types.insert(
SCRIPT_PURPOSE.to_string(),
TypeConstructor {
location: Span::empty(),
public: true,
module: "".to_string(),
parameters: vec![],
tipo: script_purpose(),
},
);
prelude prelude
} }
@ -1619,6 +1700,26 @@ pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap<DataTypeKey, TypedDa
prng_data_type, prng_data_type,
); );
// __ScriptPurpose
let script_purpose_data_type = TypedDataType::script_purpose();
data_types.insert(
DataTypeKey {
module_name: "".to_string(),
defined_type: SCRIPT_PURPOSE.to_string(),
},
script_purpose_data_type,
);
// __ScriptContext
let script_context_data_type = TypedDataType::script_context();
data_types.insert(
DataTypeKey {
module_name: "".to_string(),
defined_type: SCRIPT_CONTEXT.to_string(),
},
script_context_data_type,
);
data_types data_types
} }
@ -1711,6 +1812,28 @@ pub fn bool() -> Rc<Type> {
}) })
} }
pub fn script_purpose() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: SCRIPT_PURPOSE.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn script_context() -> Rc<Type> {
Rc::new(Type::App {
args: vec![],
public: true,
contains_opaque: false,
name: SCRIPT_CONTEXT.to_string(),
module: "".to_string(),
alias: None,
})
}
pub fn prng() -> Rc<Type> { pub fn prng() -> Rc<Type> {
Rc::new(Type::App { Rc::new(Type::App {
args: vec![], args: vec![],

View File

@ -17,7 +17,10 @@ use crate::{
FunctionAccessKey, OnTestFailure, Pattern, Span, TraceLevel, Tracing, TypedArg, FunctionAccessKey, OnTestFailure, Pattern, Span, TraceLevel, Tracing, TypedArg,
TypedClause, TypedDataType, TypedFunction, TypedPattern, TypedValidator, UnOp, TypedClause, TypedDataType, TypedFunction, TypedPattern, TypedValidator, UnOp,
}, },
builtins::{bool, byte_array, data, int, list, option, pair, void, PRELUDE}, builtins::{
bool, byte_array, data, function, int, list, option, pair, script_context, script_purpose,
void, PRELUDE, SCRIPT_PURPOSE_MINT, SCRIPT_PURPOSE_SPEND,
},
expr::TypedExpr, expr::TypedExpr,
gen_uplc::{ gen_uplc::{
air::ExpectLevel, air::ExpectLevel,
@ -142,7 +145,7 @@ impl<'a> CodeGenerator<'a> {
expressions: vec![ expressions: vec![
TypedExpr::Assignment { TypedExpr::Assignment {
location: Span::empty(), location: Span::empty(),
tipo: data(), tipo: script_context(),
value: TypedExpr::Var { value: TypedExpr::Var {
location: Span::empty(), location: Span::empty(),
constructor: ValueConstructor { constructor: ValueConstructor {
@ -150,7 +153,7 @@ impl<'a> CodeGenerator<'a> {
variant: ValueConstructorVariant::LocalVariable { variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(), location: Span::empty(),
}, },
tipo: data(), tipo: script_context(),
}, },
name: "__context__".to_string(), name: "__context__".to_string(),
} }
@ -165,7 +168,7 @@ impl<'a> CodeGenerator<'a> {
location: Span::empty(), location: Span::empty(),
value: TypedPattern::Var { value: TypedPattern::Var {
location: Span::empty(), location: Span::empty(),
name: "__purpose__".to_string(), name: "__transaction__".to_string(),
}, },
}, },
CallArg { CallArg {
@ -173,7 +176,15 @@ impl<'a> CodeGenerator<'a> {
location: Span::empty(), location: Span::empty(),
value: TypedPattern::Var { value: TypedPattern::Var {
location: Span::empty(), location: Span::empty(),
name: "__transaction__".to_string(), name: "__redeemer__".to_string(),
},
},
CallArg {
label: None,
location: Span::empty(),
value: TypedPattern::Var {
location: Span::empty(),
name: "__purpose__".to_string(),
}, },
}, },
], ],
@ -183,7 +194,7 @@ impl<'a> CodeGenerator<'a> {
field_map: None, field_map: None,
}, },
spread_location: None, spread_location: None,
tipo: data(), tipo: function(vec![data(), data(), script_purpose()], data()),
}, },
kind: AssignmentKind::let_(), kind: AssignmentKind::let_(),
}, },
@ -197,37 +208,31 @@ impl<'a> CodeGenerator<'a> {
variant: ValueConstructorVariant::LocalVariable { variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(), location: Span::empty(),
}, },
tipo: data(), tipo: script_purpose(),
}, },
name: "__purpose__".to_string(), name: "__purpose__".to_string(),
} }
.into(), .into(),
clauses: handlers clauses: handlers
.iter() .iter()
.chain(std::iter::once(fallback))
.map(|handler| { .map(|handler| {
let mut arguments = vec![]; let datum = if handler.name.as_str() == "spend" {
handler.arguments.first()
} else {
None
};
for i in 0..(handler.arguments.len() - 1) { let redeemer = handler
let argument = &handler.arguments[i]; .arguments
.get(if datum.is_some() { 1 } else { 0 })
.unwrap();
arguments.push(CallArg { let purpose_arg = handler.arguments.iter().nth_back(1).unwrap();
label: None,
location: Span::empty(), let transaction = handler.arguments.last().unwrap();
value: argument
.get_variable_name()
.map(|name| TypedPattern::Var {
location: Span::empty(),
name: name.to_string(),
})
.unwrap_or(TypedPattern::Discard {
name: "_".to_string(),
location: Span::empty(),
}),
});
}
// handler // handler
//
// spend(datum: Option<Datum>, redeemer, oref, anything) => Spend(datum, redeemer, oref, transaction) // spend(datum: Option<Datum>, redeemer, oref, anything) => Spend(datum, redeemer, oref, transaction)
// spend(datum: Datum) => Spend(datum, redeemer, oref, transaction) // spend(datum: Datum) => Spend(datum, redeemer, oref, transaction)
// spend(redeemer) => Spend(__datum__, redeemer, oref, transaction) // spend(redeemer) => Spend(__datum__, redeemer, oref, transaction)
@ -236,59 +241,133 @@ impl<'a> CodeGenerator<'a> {
"spend" => TypedPattern::Constructor { "spend" => TypedPattern::Constructor {
is_record: false, is_record: false,
location: Span::empty(), location: Span::empty(),
name: "Spend".to_string(), name: SCRIPT_PURPOSE_SPEND.to_string(),
arguments: if handler.arguments.len() == 4 { arguments: vec![
arguments CallArg {
} else { label: None,
vec![CallArg { location: Span::empty(),
value: TypedPattern::Var {
name: "__purpose_arg__".to_string(),
location: Span::empty(),
},
},
CallArg {
label: None, label: None,
location: Span::empty(), location: Span::empty(),
value: TypedPattern::Var { value: TypedPattern::Var {
name: "__datum__".to_string(), name: "__datum__".to_string(),
location: Span::empty(), location: Span::empty(),
}, },
}]
.into_iter()
.chain(arguments)
.collect()
}, },
],
module: None, module: None,
constructor: PatternConstructor::Record { constructor: PatternConstructor::Record {
name: "Spend".to_string(), name: SCRIPT_PURPOSE_SPEND.to_string(),
field_map: None, field_map: None,
}, },
spread_location: None, spread_location: None,
tipo: data(), tipo: function(
vec![data(), option(data())],
script_purpose(),
),
}, },
"mint" => TypedPattern::Constructor { "mint" => TypedPattern::Constructor {
is_record: false, is_record: false,
location: Span::empty(), location: Span::empty(),
name: "Mint".to_string(), name: SCRIPT_PURPOSE_MINT.to_string(),
arguments, arguments: vec![CallArg {
label: None,
location: Span::empty(),
value: TypedPattern::Var {
name: "__purpose_arg__".to_string(),
location: Span::empty(),
},
}],
module: None, module: None,
constructor: PatternConstructor::Record { constructor: PatternConstructor::Record {
name: "Mint".to_string(), name: SCRIPT_PURPOSE_MINT.to_string(),
field_map: None, field_map: None,
}, },
spread_location: None, spread_location: None,
tipo: data(), tipo: function(vec![data()], script_purpose()),
},
_ => TypedPattern::Var {
location: Span::empty(),
name: "__context__".to_string(),
}, },
purpose => {
unreachable!("unexpected/unknown purpose: {:?}", purpose)
}
}; };
let mut then = vec![]; let mut then = vec![];
let last_arg = handler.arguments.last().unwrap(); // expect redeemer: tipo = __redeemer__
// let last_arg_name = __transaction__
if let Some(last_arg_name) = last_arg.get_variable_name() {
then.push(TypedExpr::Assignment { then.push(TypedExpr::Assignment {
location: Span::empty(), location: Span::empty(),
tipo: redeemer.tipo.clone(),
value: TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: data(), tipo: data(),
},
name: "__redeemer__".to_string(),
}
.into(),
pattern: TypedPattern::Var {
location: Span::empty(),
name: redeemer
.get_variable_name()
.unwrap_or("_")
.to_string(),
},
kind: if redeemer.tipo.is_data() {
AssignmentKind::let_()
} else {
AssignmentKind::expect()
},
});
// Cast the datum, if any
if let Some(datum) = datum {
// expect datum: tipo = __datum__
then.push(TypedExpr::Assignment {
location: Span::empty(),
tipo: datum.tipo.clone(),
value: TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: option(data()),
},
name: "__datum__".to_string(),
}
.into(),
pattern: TypedPattern::Var {
location: Span::empty(),
name: datum
.get_variable_name()
.unwrap_or("_")
.to_string(),
},
kind: if redeemer.tipo.is_data() {
AssignmentKind::let_()
} else {
AssignmentKind::expect()
},
});
}
// let last_arg_name = __transaction__
if let Some(arg_name) = transaction.get_variable_name() {
then.push(TypedExpr::Assignment {
location: Span::empty(),
tipo: purpose_arg.tipo.clone(),
value: TypedExpr::Var { value: TypedExpr::Var {
location: Span::empty(), location: Span::empty(),
constructor: ValueConstructor { constructor: ValueConstructor {
@ -303,97 +382,12 @@ impl<'a> CodeGenerator<'a> {
.into(), .into(),
pattern: TypedPattern::Var { pattern: TypedPattern::Var {
location: Span::empty(), location: Span::empty(),
name: last_arg_name.to_string(), name: arg_name.to_string(),
}, },
kind: AssignmentKind::let_(), kind: AssignmentKind::let_(),
}); });
} }
let first_arg = handler.arguments.first().unwrap();
if handler.name.as_str() == "spend" {
if handler.arguments.len() == 3 {
// implicit None
then.push(TypedExpr::Assignment {
location: Span::empty(),
tipo: option(data()),
value: TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant:
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: option(data()),
},
name: "__datum__".to_string(),
}
.into(),
pattern: TypedPattern::Constructor {
is_record: false,
location: Span::empty(),
name: "None".to_string(),
arguments: vec![],
module: None,
constructor: PatternConstructor::Record {
name: "None".to_string(),
field_map: None,
},
spread_location: None,
tipo: option(data()),
},
kind: AssignmentKind::expect(),
})
} else if !first_arg.tipo.is_option() {
// implicit Some
then.push(TypedExpr::Assignment {
location: Span::empty(),
tipo: option(data()),
value: TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant:
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: option(data()),
},
name: first_arg.get_name(),
}
.into(),
pattern: TypedPattern::Constructor {
is_record: false,
location: Span::empty(),
name: "Some".to_string(),
arguments: vec![CallArg {
label: None,
location: Span::empty(),
value: first_arg
.get_variable_name()
.map(|name| TypedPattern::Var {
location: Span::empty(),
name: name.to_string(),
})
.unwrap_or(TypedPattern::Discard {
name: "_".to_string(),
location: Span::empty(),
}),
}],
module: None,
constructor: PatternConstructor::Record {
name: "Some".to_string(),
field_map: None,
},
spread_location: None,
tipo: option(first_arg.tipo.clone()),
},
kind: AssignmentKind::expect(),
})
}
}
then.push(handler.body.clone()); then.push(handler.body.clone());
TypedClause { TypedClause {
@ -405,6 +399,56 @@ impl<'a> CodeGenerator<'a> {
}, },
} }
}) })
// FIXME: This is only needed if there's non-exhaustive patterns
// above.
.chain(std::iter::once(fallback).map(|fallback| {
let pattern = TypedPattern::Var {
location: Span::empty(),
name: "__context__".to_string(),
};
let arg = fallback.arguments.first().unwrap();
let then = vec![
TypedExpr::Assignment {
location: Span::empty(),
tipo: arg.tipo.clone(),
value: TypedExpr::Var {
location: Span::empty(),
constructor: ValueConstructor {
public: true,
variant: ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
tipo: arg.tipo.clone(),
},
name: "__context__".to_string(),
}
.into(),
pattern: arg
.get_variable_name()
.map(|name| TypedPattern::Var {
location: Span::empty(),
name: name.to_string(),
})
.unwrap_or(TypedPattern::Discard {
name: "__context__".to_string(),
location: Span::empty(),
}),
kind: AssignmentKind::let_(),
},
fallback.body.clone(),
];
TypedClause {
location: Span::empty(),
pattern,
then: TypedExpr::Sequence {
location: Span::empty(),
expressions: then,
},
}
}))
.collect(), .collect(),
}, },
], ],
@ -419,16 +463,15 @@ impl<'a> CodeGenerator<'a> {
on_test_failure: OnTestFailure::FailImmediately, on_test_failure: OnTestFailure::FailImmediately,
}; };
let mut air_tree_fun = self.build(&fun.body, module_name, &[]); let mut air_tree_fun = AirTree::anon_func(
vec!["__context__".to_string()],
self.build(&fun.body, module_name, &[]),
true,
);
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing); air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
let (src_code, lines) = self.module_src.get(module_name).unwrap(); let validator_args_tree = AirTree::no_op(air_tree_fun);
let mut validator_args_tree =
self.check_validator_args(&fun.arguments, true, air_tree_fun, src_code, lines);
validator_args_tree = AirTree::no_op(validator_args_tree);
let full_tree = self.hoist_functions_to_validator(validator_args_tree); let full_tree = self.hoist_functions_to_validator(validator_args_tree);
@ -1624,7 +1667,13 @@ impl<'a> CodeGenerator<'a> {
type_map.insert(index, field_type); type_map.insert(index, field_type);
} }
assert!(type_map.len() >= arguments.len()); assert!(
type_map.len() >= arguments.len(),
"type map len: {}, arguments len: {}; for constructor {:?}",
type_map.len(),
arguments.len(),
name,
);
let mut fields = vec![]; let mut fields = vec![];
@ -1752,7 +1801,10 @@ impl<'a> CodeGenerator<'a> {
} else { } else {
assert!( assert!(
data_type.constructors.len() == 1, data_type.constructors.len() == 1,
"data_type={data_type:#?}" "attempted expect on a type with more or less than 1 constructor: \nis_expect? {}\nfull_check? {}\ndata_type={data_type:#?}\n{}",
props.kind.is_expect(),
props.full_check,
name,
); );
then then
}; };
@ -3030,7 +3082,11 @@ impl<'a> CodeGenerator<'a> {
) )
}); });
assert!(!data_type.constructors.is_empty()); assert!(
!data_type.constructors.is_empty(),
"{}\n{constructor:#?}\n{data_type:#?}",
subject_tipo.to_pretty(0)
);
let (constr_index, _) = data_type let (constr_index, _) = data_type
.constructors .constructors

View File

@ -294,7 +294,7 @@ mod tests {
let validators = Validator::from_checked_module(&modules, &mut generator, validator, def, &PlutusVersion::default()); let validators = Validator::from_checked_module(&modules, &mut generator, validator, def, &PlutusVersion::default());
if validators.len() > 1 { if validators.len() > 2 {
panic!("Multi-validator given to test bench. Don't do that.") panic!("Multi-validator given to test bench. Don't do that.")
} }
@ -395,7 +395,7 @@ mod tests {
assert_validator!( assert_validator!(
r#" r#"
validator thing { validator thing {
mint(redeemer: Data, ctx: Data) { mint(redeemer: Data, policy_id: Data, transaction: Data) {
True True
} }
} }