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,
];
pub const VALIDATOR_ELSE: &str = "else";
// ----------------------------------------------------------------------------
// Types

View File

@ -1575,7 +1575,11 @@ impl<'a> CodeGenerator<'a> {
otherwise: AirTree,
depth: usize,
) -> 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
// this function is called from anywhere else besides assignment
let tipo = &convert_opaque_type(tipo, &self.data_types, true);

View File

@ -1,6 +1,6 @@
---
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 {
error: Error {
@ -16,7 +16,7 @@ Schema {
},
],
},
location: 26..37,
location: 28..39,
source_code: NamedSource {
name: "",
source: "<redacted>",

View File

@ -69,15 +69,21 @@ impl Validator {
));
}
validators.push(Validator::create_validator_blueprint(
generator,
modules,
module,
def,
&def.fallback,
&mut program,
plutus_version,
));
// NOTE: Only push the fallback if all other validators have been successfully
// generated. Otherwise, we may fall into scenarios where we cannot generate validators
// (e.g. due to the presence of generics in datum/redeemer), which won't be caught by
// the else branch since it lacks arguments.
if validators.iter().all(|v| v.is_ok()) {
validators.push(Validator::create_validator_blueprint(
generator,
modules,
module,
def,
&def.fallback,
&mut program,
plutus_version,
));
}
validators
}
@ -92,10 +98,6 @@ impl Validator {
program: &mut MemoProgram,
plutus_version: &PlutusVersion,
) -> 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 parameters = def
@ -122,72 +124,83 @@ impl Validator {
})
.collect::<Result<_, _>>()?;
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!");
};
let (datum, redeemer) = if func.name == well_known::VALIDATOR_ELSE {
(None, None)
} else {
let mut args = func.arguments.iter().rev();
Annotated::from_type(
modules.into(),
tipo_or_annotation(module, &TypedArg {
arg_name: datum.arg_name.clone(),
let (_, _, redeemer, datum) = (
args.next(),
args.next(),
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,
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,
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(),
),
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 = 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: redeemer.map(|redeemer| redeemer.arg_name.get_label()),
title: Some(redeemer.arg_name.get_label()),
schema,
});
})?;
(datum, Some(redeemer))
};
Ok(Validator {
title: format!(
"{}.{}_{}",
&module.name,
&def.name,
if func.name == "else" {
if func.name == well_known::VALIDATOR_ELSE {
"__fallback"
} else {
&func.name