Fix generation of fallback validator

This must only happen in case all other validator succeed; otherwise
  we might generate invalid validators.
This commit is contained in:
KtorZ 2024-08-24 11:15:56 +02:00
parent 73522296aa
commit 442010d056
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
4 changed files with 89 additions and 70 deletions

View File

@ -49,6 +49,8 @@ pub const SCRIPT_PURPOSE_CONSTRUCTORS: &[&str] = &[
SCRIPT_PURPOSE_PROPOSE, SCRIPT_PURPOSE_PROPOSE,
]; ];
pub const VALIDATOR_ELSE: &str = "else";
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Types // Types

View File

@ -1575,7 +1575,11 @@ impl<'a> CodeGenerator<'a> {
otherwise: AirTree, otherwise: AirTree,
depth: usize, depth: usize,
) -> AirTree { ) -> AirTree {
assert!(tipo.get_generic().is_none()); assert!(
tipo.get_generic().is_none(),
"left-hand side of expect is generic: {}",
tipo.to_pretty(0)
);
// Shouldn't be needed but still here just in case // Shouldn't be needed but still here just in case
// this function is called from anywhere else besides assignment // this function is called from anywhere else besides assignment
let tipo = &convert_opaque_type(tipo, &self.data_types, true); let tipo = &convert_opaque_type(tipo, &self.data_types, true);

View File

@ -1,6 +1,6 @@
--- ---
source: crates/aiken-project/src/blueprint/validator.rs source: crates/aiken-project/src/blueprint/validator.rs
description: "Code:\n\nvalidator {\n fn generics(redeemer: a, ctx: Void) {\n True\n }\n}\n" description: "Code:\n\nvalidator generics {\n mint(redeemer: a, policy_id: ByteArray, transaction: Data) {\n True\n }\n}\n"
--- ---
Schema { Schema {
error: Error { error: Error {
@ -16,7 +16,7 @@ Schema {
}, },
], ],
}, },
location: 26..37, location: 28..39,
source_code: NamedSource { source_code: NamedSource {
name: "", name: "",
source: "<redacted>", source: "<redacted>",

View File

@ -69,15 +69,21 @@ impl Validator {
)); ));
} }
validators.push(Validator::create_validator_blueprint( // NOTE: Only push the fallback if all other validators have been successfully
generator, // generated. Otherwise, we may fall into scenarios where we cannot generate validators
modules, // (e.g. due to the presence of generics in datum/redeemer), which won't be caught by
module, // the else branch since it lacks arguments.
def, if validators.iter().all(|v| v.is_ok()) {
&def.fallback, validators.push(Validator::create_validator_blueprint(
&mut program, generator,
plutus_version, modules,
)); module,
def,
&def.fallback,
&mut program,
plutus_version,
));
}
validators validators
} }
@ -92,10 +98,6 @@ impl Validator {
program: &mut MemoProgram, program: &mut MemoProgram,
plutus_version: &PlutusVersion, plutus_version: &PlutusVersion,
) -> Result<Validator, Error> { ) -> Result<Validator, Error> {
let mut args = func.arguments.iter().rev();
let (_, _, redeemer, datum) = (args.next(), args.next(), args.next(), args.next());
let mut definitions = Definitions::new(); let mut definitions = Definitions::new();
let parameters = def let parameters = def
@ -122,72 +124,83 @@ impl Validator {
}) })
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
let datum = datum let (datum, redeemer) = if func.name == well_known::VALIDATOR_ELSE {
.map(|datum| { (None, None)
match datum.tipo.as_ref() { } else {
Type::App { module: module_name, name, args, .. } if module_name.is_empty() && name == well_known::OPTION => { let mut args = func.arguments.iter().rev();
let Some(Annotation::Constructor { arguments, .. }) = datum.annotation.as_ref() else {
panic!("Datum isn't an option but should be; this should have been caught by the type-checker!");
};
Annotated::from_type( let (_, _, redeemer, datum) = (
modules.into(), args.next(),
tipo_or_annotation(module, &TypedArg { args.next(),
arg_name: datum.arg_name.clone(), args.next().expect("redeemer is always present"),
args.next(),
);
let datum = datum
.map(|datum| {
match datum.tipo.as_ref() {
Type::App { module: module_name, name, args, .. } if module_name.is_empty() && name == well_known::OPTION => {
let Some(Annotation::Constructor { arguments, .. }) = datum.annotation.as_ref() else {
panic!("Datum isn't an option but should be; this should have been caught by the type-checker!");
};
Annotated::from_type(
modules.into(),
tipo_or_annotation(module, &TypedArg {
arg_name: datum.arg_name.clone(),
location: datum.location,
annotation: arguments.first().cloned(),
doc: datum.doc.clone(),
is_validator_param: datum.is_validator_param,
tipo: args.first().expect("Option always have a single type argument.").clone()
}),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: datum.location, location: datum.location,
annotation: arguments.first().cloned(), source_code: NamedSource::new(
doc: datum.doc.clone(), module.input_path.display().to_string(),
is_validator_param: datum.is_validator_param, module.code.clone(),
tipo: args.first().expect("Option always have a single type argument.").clone() ),
}), })
&mut definitions, },
) _ => panic!("Datum isn't an option but should be; this should have been caught by the type-checker!"),
.map_err(|error| Error::Schema { }
error,
location: datum.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
})
},
_ => panic!("Datum isn't an option but should be; this should have been caught by the type-checker!"),
}
})
.transpose()?
.map(|schema| Parameter {
title: datum.map(|datum| datum.arg_name.get_label()),
schema,
});
let redeemer = redeemer
.map(|redeemer| {
Annotated::from_type(
modules.into(),
tipo_or_annotation(module, redeemer),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: redeemer.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
}) })
.transpose()?
.map(|schema| Parameter {
title: datum.map(|datum| datum.arg_name.get_label()),
schema,
});
let redeemer = Annotated::from_type(
modules.into(),
tipo_or_annotation(module, redeemer),
&mut definitions,
)
.map_err(|error| Error::Schema {
error,
location: redeemer.location,
source_code: NamedSource::new(
module.input_path.display().to_string(),
module.code.clone(),
),
}) })
.transpose()?
.map(|schema| Parameter { .map(|schema| Parameter {
title: redeemer.map(|redeemer| redeemer.arg_name.get_label()), title: Some(redeemer.arg_name.get_label()),
schema, schema,
}); })?;
(datum, Some(redeemer))
};
Ok(Validator { Ok(Validator {
title: format!( title: format!(
"{}.{}_{}", "{}.{}_{}",
&module.name, &module.name,
&def.name, &def.name,
if func.name == "else" { if func.name == well_known::VALIDATOR_ELSE {
"__fallback" "__fallback"
} else { } else {
&func.name &func.name