Fix: issue crash in code gen with incorrect column length in decision trees (#1069)

* Fix: Deeply nested assignments would offset the new columns count calculation. Now we track relevant columns and their path to ensure each row has wildcards if they don't contain the relevant column

* Add test plus clippy fix

* Clippy fix

* New version clippy fix
This commit is contained in:
Kasey 2024-12-05 11:02:19 +07:00 committed by GitHub
parent a9675fedc6
commit 86ec3b2924
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 392 additions and 183 deletions

View File

@ -881,7 +881,7 @@ pub enum Located<'a> {
Annotation(&'a Annotation),
}
impl<'a> Located<'a> {
impl Located<'_> {
pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
match self {
Self::Expression(expression) => expression.definition_location(),
@ -1996,7 +1996,7 @@ impl<'de> serde::Deserialize<'de> for Bls12_381Point {
{
struct FieldVisitor;
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
impl serde::de::Visitor<'_> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {

View File

@ -3,7 +3,7 @@ use pretty::RcDoc;
use std::{cmp::Ordering, fmt::Display, rc::Rc};
use indexmap::IndexMap;
use itertools::{Itertools, Position};
use itertools::{Either, Itertools, Position};
use crate::{
ast::{DataTypeKey, Pattern, TypedClause, TypedDataType, TypedPattern},
@ -12,10 +12,6 @@ use crate::{
use super::{interner::AirInterner, tree::AirTree};
const PAIR_NEW_COLUMNS: usize = 2;
const MIN_NEW_COLUMNS: usize = 1;
#[derive(Clone, Default, Copy)]
struct Occurrence {
passed_wild_card: bool,
@ -71,6 +67,26 @@ impl PartialEq for Path {
impl Eq for Path {}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Path {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Path::Pair(a), Path::Pair(b))
| (Path::Tuple(a), Path::Tuple(b))
| (Path::List(a), Path::List(b))
| (Path::ListTail(a), Path::ListTail(b))
| (Path::Constr(_, a), Path::Constr(_, b)) => a.cmp(b),
(Path::OpaqueConstr(_), Path::OpaqueConstr(_)) => Ordering::Equal,
_ => Ordering::Equal,
}
}
}
#[derive(Clone, Debug)]
pub struct Assigned {
pub path: Vec<Path>,
@ -568,7 +584,7 @@ impl<'a> DecisionTree<'a> {
}
}
impl<'a> Display for DecisionTree<'a> {
impl Display for DecisionTree<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_pretty())
}
@ -603,33 +619,86 @@ impl<'a, 'b> TreeGen<'a, 'b> {
) -> DecisionTree<'a> {
let mut hoistables = IndexMap::new();
let rows = clauses
.iter()
.enumerate()
.map(|(index, clause)| {
// Assigns are split out from patterns so they can be handled
// outside of the tree algorithm
let (assign, row_items) =
self.map_pattern_to_row(&clause.pattern, subject_tipo, vec![]);
let mut columns_added = vec![];
self.interner.intern(format!("__clause_then_{}", index));
let clause_then_name = self
.interner
.lookup_interned(&format!("__clause_then_{}", index));
let rows = {
let rows_initial = clauses
.iter()
.enumerate()
.map(|(index, clause)| {
// Assigns are split out from patterns so they can be handled
// outside of the tree algorithm
let (assign, row_items) =
self.map_pattern_to_row(&clause.pattern, subject_tipo, vec![]);
hoistables.insert(clause_then_name.clone(), (vec![], &clause.then));
self.interner.intern(format!("__clause_then_{}", index));
let clause_then_name = self
.interner
.lookup_interned(&format!("__clause_then_{}", index));
let row = Row {
assigns: assign.into_iter().collect_vec(),
columns: row_items,
then: clause_then_name,
};
hoistables.insert(clause_then_name.clone(), (vec![], &clause.then));
self.interner.pop_text(format!("__clause_then_{}", index));
// Some good ol' mutation to track added columns per relevant path
// relevant path indicating a column that has a pattern to test at some point in
// one of the rows
row_items.iter().for_each(|col| {
if !columns_added.contains(&col.path) {
columns_added.push(col.path.clone());
}
});
row
})
.collect_vec();
let row = Row {
assigns: assign.into_iter().collect_vec(),
columns: row_items,
then: clause_then_name,
};
self.interner.pop_text(format!("__clause_then_{}", index));
row
})
.collect_vec();
columns_added = columns_added
.into_iter()
.sorted_by(|a, b| {
let mut a = a.clone();
let mut b = b.clone();
// It's impossible for duplicates since we check before insertion
while let Ordering::Equal = a.first().cmp(&b.first()) {
a.remove(0);
b.remove(0);
}
a.first().cmp(&b.first())
})
.map(|col| {
// remove opaqueconstr paths since they are just type information
col.into_iter()
.filter(|a| !matches!(a, Path::OpaqueConstr(_)))
.collect_vec()
})
.collect_vec();
rows_initial
.into_iter()
.map(|mut row| {
for (index, path) in columns_added.iter().enumerate() {
if !row
.columns
.get(index)
.map(|col| col.path == *path)
.unwrap_or(false)
{
row.columns.insert(index, self.wild_card_pattern.clone());
}
}
row
})
.collect_vec()
};
let mut tree = self.do_build_tree(subject_tipo, PatternMatrix { rows }, &mut hoistables);
@ -654,6 +723,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
then_map: &mut IndexMap<String, (Vec<Assigned>, &'a TypedExpr)>,
) -> DecisionTree<'a> {
let column_length = matrix.rows[0].columns.len();
// First step make sure all rows have same number of columns
// or something went wrong
assert!(matrix
@ -734,6 +804,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
.unwrap();
let specialized_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
let mut relevant_columns: Vec<(CaseTest, Vec<Vec<Path>>)> = vec![];
// Time to split on the matrices based on case to test for or lack of
let (default_matrix, specialized_matrices) = matrix.rows.into_iter().fold(
@ -841,37 +912,37 @@ impl<'a, 'b> TreeGen<'a, 'b> {
// Add inner patterns to existing row
let mut new_cols = remaining_patts.into_iter().flat_map(|x| x.1).collect_vec();
// To align number of columns we pop off the tail since it can
// never include a pattern besides wild card
if matches!(case, CaseTest::ListWithTail(_)) {
new_cols.pop();
}
let new_paths = new_cols.iter().map(|col| col.path.clone()).collect_vec();
let added_columns = new_cols.len();
// Pop off tail so that it aligns more easily with other list patterns
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
new_paths.iter().for_each(|col| {
if !a.1.contains(col) {
a.1.push(col.clone());
}
});
} else {
relevant_columns.push((case.clone(), new_paths.clone()));
};
new_cols.extend(row.columns);
row.columns = new_cols;
if let CaseTest::Wild = case {
let current_wild_cols = row.columns.len();
default_matrix.push(row.clone());
case_matrices.iter_mut().for_each(|(_, matrix)| {
let mut row = row.clone();
let total_cols = matrix[0].columns.len();
case_matrices.iter_mut().for_each(|(case, matrix)| {
let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == *case) else {
unreachable!()
};
if total_cols != 0 {
let added_columns = total_cols - current_wild_cols;
for _ in 0..added_columns {
row.columns.insert(0, self.wild_card_pattern.clone());
new_paths.iter().for_each(|col| {
if !a.1.contains(col) {
a.1.push(col.clone());
}
});
matrix.push(row);
}
matrix.push(row.clone());
});
} else if let CaseTest::ListWithTail(tail_case_length) = case {
// For lists with tail it's a special case where we also add it to existing patterns
@ -886,20 +957,30 @@ impl<'a, 'b> TreeGen<'a, 'b> {
for elem_count in tail_case_length..=longest_elems_no_tail {
let case = CaseTest::List(elem_count);
let mut row = row.clone();
// paths first
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
new_paths.iter().for_each(|col| {
if !a.1.contains(col) {
a.1.push(col.clone());
}
});
} else {
relevant_columns.push((case.clone(), new_paths.clone()));
};
for _ in 0..(elem_count - tail_case_length) {
row.columns
.insert(tail_case_length, self.wild_card_pattern.clone());
let row = row.clone();
// now insertion into the appropriate matrix
if let Some(entry) =
case_matrices.iter_mut().find(|item| item.0 == case)
{
entry.1.push(row);
} else {
let mut rows = default_matrix.to_vec();
rows.push(row);
case_matrices.push((case, rows));
}
self.insert_case(
&mut case_matrices,
case,
&default_matrix,
row,
added_columns,
);
}
}
@ -907,35 +988,123 @@ impl<'a, 'b> TreeGen<'a, 'b> {
for elem_count in tail_case_length..=longest_elems_with_tail {
let case = CaseTest::ListWithTail(elem_count);
let mut row = row.clone();
if let Some(a) = relevant_columns.iter_mut().find(|a| a.0 == case) {
new_paths.iter().for_each(|col| {
if !a.1.contains(col) {
a.1.push(col.clone());
}
});
} else {
relevant_columns.push((case.clone(), new_paths.clone()));
};
for _ in 0..(elem_count - tail_case_length) {
row.columns
.insert(tail_case_length, self.wild_card_pattern.clone());
let row = row.clone();
if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
entry.1.push(row);
} else {
let mut rows = default_matrix.clone();
rows.push(row);
case_matrices.push((case, rows));
}
self.insert_case(
&mut case_matrices,
case,
&default_matrix,
row,
added_columns,
);
}
} else if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
entry.1.push(row);
} else {
self.insert_case(
&mut case_matrices,
case,
&default_matrix,
row,
added_columns,
);
let mut rows = default_matrix.clone();
rows.push(row);
case_matrices.push((case, rows));
}
(default_matrix, case_matrices)
},
);
let (default_relevant_cols, relevant_columns): (Vec<_>, Vec<_>) = relevant_columns
.into_iter()
.map(|(case, paths)| {
(
case,
paths
.into_iter()
.sorted_by(|a, b| {
let mut a = a.clone();
let mut b = b.clone();
// It's impossible for duplicates since we check before insertion
while let Ordering::Equal = a.first().cmp(&b.first()) {
a.remove(0);
b.remove(0);
}
a.first().cmp(&b.first())
})
.map(|col| {
// remove opaqueconstr paths since they are just type information
col.into_iter()
.filter(|a| !matches!(a, Path::OpaqueConstr(_)))
.collect_vec()
})
.collect_vec(),
)
})
.partition_map(|(case, paths)| match case {
CaseTest::Wild => Either::Left(paths),
_ => Either::Right((case, paths)),
});
let default_matrix = default_matrix
.into_iter()
.map(|mut row| {
for (index, path) in default_relevant_cols[0].iter().enumerate() {
if !row
.columns
.get(index)
.map(|col| col.path == *path)
.unwrap_or(false)
{
row.columns.insert(index, self.wild_card_pattern.clone());
}
}
row
})
.collect_vec();
let specialized_matrices = specialized_matrices
.into_iter()
.map(|(case, matrix)| {
let Some((_, relevant_cols)) = relevant_columns
.iter()
.find(|(relevant_case, _)| relevant_case == &case)
else {
unreachable!()
};
(
case,
matrix
.into_iter()
.map(|mut row| {
for (index, path) in relevant_cols.iter().enumerate() {
if !row
.columns
.get(index)
.map(|col| col.path == *path)
.unwrap_or(false)
{
row.columns.insert(index, self.wild_card_pattern.clone());
}
}
row
})
.collect_vec(),
)
})
.collect_vec();
let default_matrix = PatternMatrix {
rows: default_matrix,
};
@ -1001,39 +1170,13 @@ impl<'a, 'b> TreeGen<'a, 'b> {
) -> (Vec<Assigned>, Vec<RowItem<'a>>) {
let current_tipo = get_tipo_by_path(subject_tipo.clone(), &path);
let new_columns_added = if current_tipo.is_pair() {
PAIR_NEW_COLUMNS
} else if current_tipo.is_tuple() {
let Type::Tuple { elems, .. } = current_tipo.as_ref() else {
unreachable!("{:#?}", current_tipo)
};
elems.len()
} else if let Some(data) = lookup_data_type_by_tipo(self.data_types, &current_tipo) {
if data.constructors.len() == 1 {
data.constructors[0].arguments.len()
} else if data.is_never() {
0
} else {
MIN_NEW_COLUMNS
}
} else {
MIN_NEW_COLUMNS
};
match pattern {
Pattern::Var { name, .. } => (
vec![Assigned {
path: path.clone(),
assigned: name.clone(),
}],
vec![RowItem {
pattern,
path: path.clone(),
}]
.into_iter()
.cycle()
.take(new_columns_added)
.collect_vec(),
vec![],
),
Pattern::Assign { name, pattern, .. } => {
@ -1049,19 +1192,14 @@ impl<'a, 'b> TreeGen<'a, 'b> {
);
(assigns, patts)
}
Pattern::Int { .. }
| Pattern::ByteArray { .. }
| Pattern::Discard { .. }
| Pattern::List { .. } => (
Pattern::Discard { .. } => (vec![], vec![]),
Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::List { .. } => (
vec![],
vec![RowItem {
pattern,
path: path.clone(),
}]
.into_iter()
.cycle()
.take(new_columns_added)
.collect_vec(),
}],
),
Pattern::Constructor {
@ -1101,11 +1239,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
vec![RowItem {
pattern,
path: path.clone(),
}]
.into_iter()
.cycle()
.take(new_columns_added)
.collect_vec(),
}],
)
}
}
@ -1126,6 +1260,7 @@ impl<'a, 'b> TreeGen<'a, 'b> {
(assigns, patts)
}
Pattern::Tuple { elems, .. } => {
elems
.iter()
@ -1146,30 +1281,6 @@ impl<'a, 'b> TreeGen<'a, 'b> {
}
}
}
fn insert_case(
&self,
case_matrices: &mut Vec<(CaseTest, Vec<Row<'a>>)>,
case: CaseTest,
default_matrix: &[Row<'a>],
new_row: Row<'a>,
added_columns: usize,
) {
if let Some(entry) = case_matrices.iter_mut().find(|item| item.0 == case) {
entry.1.push(new_row);
} else {
let mut rows = default_matrix.to_vec();
for _ in 0..added_columns {
for row in &mut rows {
row.columns.insert(0, self.wild_card_pattern.clone());
}
}
rows.push(new_row);
case_matrices.push((case, rows));
}
}
}
pub fn get_tipo_by_path(mut subject_tipo: Rc<Type>, mut path: &[Path]) -> Rc<Type> {

View File

@ -622,7 +622,7 @@ pub struct Counterexample<'a> {
pub cache: Cache<'a, PlutusData>,
}
impl<'a> Counterexample<'a> {
impl Counterexample<'_> {
fn consider(&mut self, choices: &[u8]) -> bool {
if choices == self.choices {
return true;

View File

@ -1284,7 +1284,7 @@ fn suggest_pattern(
}
}
fn suggest_generic(name: &String, expected: usize) -> String {
fn suggest_generic(name: &str, expected: usize) -> String {
if expected == 0 {
return name.to_doc().to_pretty_string(70);
}

View File

@ -206,7 +206,7 @@ impl Printer {
}
}
fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> {
fn qualify_type_name(module: &str, typ_name: &str) -> Document<'static> {
if module.is_empty() {
docvec!["aiken.", Document::String(typ_name.to_string())]
} else {

View File

@ -52,7 +52,7 @@ struct ModuleTemplate<'a> {
timestamp: String,
}
impl<'a> ModuleTemplate<'a> {
impl ModuleTemplate<'_> {
pub fn is_current_module(&self, module: &DocLink) -> bool {
match module.path.split(".html").next() {
None => false,
@ -75,7 +75,7 @@ struct PageTemplate<'a> {
timestamp: &'a str,
}
impl<'a> PageTemplate<'a> {
impl PageTemplate<'_> {
pub fn is_current_module(&self, _module: &DocLink) -> bool {
false
}

View File

@ -720,7 +720,7 @@ struct DisplayWarning<'a> {
help: Option<String>,
}
impl<'a> Diagnostic for DisplayWarning<'a> {
impl Diagnostic for DisplayWarning<'_> {
fn severity(&self) -> Option<miette::Severity> {
Some(miette::Severity::Warning)
}
@ -749,7 +749,7 @@ impl<'a> Diagnostic for DisplayWarning<'a> {
}
}
impl<'a> Debug for DisplayWarning<'a> {
impl Debug for DisplayWarning<'_> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unreachable!("Display warning are never shown directly.");
}

View File

@ -74,7 +74,7 @@ impl<'de> Deserialize<'de> for PackageName {
{
struct PackageNameVisitor;
impl<'de> Visitor<'de> for PackageNameVisitor {
impl Visitor<'_> for PackageNameVisitor {
type Value = PackageName;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {

View File

@ -191,7 +191,7 @@ enum Log<'a> {
Done(&'a Shell),
}
impl<'a> Display for Log<'a> {
impl Display for Log<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
match self {
Log::Detecting(what) => pretty::fmt_step(f, "Detecting", what),

View File

@ -632,7 +632,7 @@ fn encode_type(typ: &Type, bytes: &mut Vec<u8>) {
}
}
impl<'b> Decode<'b> for Constant {
impl Decode<'_> for Constant {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
match &decode_constant(d)?[..] {
[0] => Ok(Constant::Integer(BigInt::decode(d)?)),
@ -806,7 +806,7 @@ impl Encode for Unique {
}
}
impl<'b> Decode<'b> for Unique {
impl Decode<'_> for Unique {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
Ok(isize::decode(d)?.into())
}
@ -821,7 +821,7 @@ impl Encode for Name {
}
}
impl<'b> Decode<'b> for Name {
impl Decode<'_> for Name {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
Ok(Name {
text: String::decode(d)?,
@ -830,7 +830,7 @@ impl<'b> Decode<'b> for Name {
}
}
impl<'b> Binder<'b> for Name {
impl Binder<'_> for Name {
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error> {
self.encode(e)?;
@ -855,7 +855,7 @@ impl Encode for NamedDeBruijn {
}
}
impl<'b> Decode<'b> for NamedDeBruijn {
impl Decode<'_> for NamedDeBruijn {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
Ok(NamedDeBruijn {
text: String::decode(d)?,
@ -864,7 +864,7 @@ impl<'b> Decode<'b> for NamedDeBruijn {
}
}
impl<'b> Binder<'b> for NamedDeBruijn {
impl Binder<'_> for NamedDeBruijn {
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error> {
self.text.encode(e)?;
self.index.encode(e)?;
@ -892,13 +892,13 @@ impl Encode for DeBruijn {
}
}
impl<'b> Decode<'b> for DeBruijn {
impl Decode<'_> for DeBruijn {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
Ok(usize::decode(d)?.into())
}
}
impl<'b> Binder<'b> for DeBruijn {
impl Binder<'_> for DeBruijn {
fn binder_encode(&self, _: &mut Encoder) -> Result<(), en::Error> {
Ok(())
}
@ -922,7 +922,7 @@ impl Encode for FakeNamedDeBruijn {
}
}
impl<'b> Decode<'b> for FakeNamedDeBruijn {
impl Decode<'_> for FakeNamedDeBruijn {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
let index = DeBruijn::decode(d)?;
@ -930,7 +930,7 @@ impl<'b> Decode<'b> for FakeNamedDeBruijn {
}
}
impl<'b> Binder<'b> for FakeNamedDeBruijn {
impl Binder<'_> for FakeNamedDeBruijn {
fn binder_encode(&self, _: &mut Encoder) -> Result<(), en::Error> {
Ok(())
}
@ -954,7 +954,7 @@ impl Encode for DefaultFunction {
}
}
impl<'b> Decode<'b> for DefaultFunction {
impl Decode<'_> for DefaultFunction {
fn decode(d: &mut Decoder) -> Result<Self, de::Error> {
let builtin_tag = d.bits8(BUILTIN_TAG_WIDTH as usize)?;
builtin_tag.try_into()

View File

@ -132,7 +132,7 @@ impl ToPlutusData for Address {
}
}
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, TransactionInput> {
impl ToPlutusData for WithWrappedTransactionId<'_, TransactionInput> {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
@ -197,7 +197,7 @@ where
}
}
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
impl ToPlutusData for WithWrappedTransactionId<'_, KeyValuePairs<ScriptPurpose, Redeemer>> {
fn to_plutus_data(&self) -> PlutusData {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
for (key, value) in self.0.iter() {
@ -210,7 +210,7 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, KeyValuePairs<ScriptPurpo
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Vec<Certificate>> {
impl ToPlutusData for WithNeverRegistrationDeposit<'_, Vec<Certificate>> {
fn to_plutus_data(&self) -> PlutusData {
self.0
.iter()
@ -220,7 +220,7 @@ impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Vec<Certificate>> {
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, KeyValuePairs<ScriptPurpose, Redeemer>> {
impl ToPlutusData for WithNeverRegistrationDeposit<'_, KeyValuePairs<ScriptPurpose, Redeemer>> {
fn to_plutus_data(&self) -> PlutusData {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
for (key, value) in self.0.iter() {
@ -309,7 +309,7 @@ impl ToPlutusData for PositiveCoin {
}
}
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Value> {
impl ToPlutusData for WithZeroAdaAsset<'_, Value> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
Value::Coin(coin) => {
@ -345,7 +345,7 @@ impl ToPlutusData for Value {
}
}
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, MintValue> {
impl ToPlutusData for WithZeroAdaAsset<'_, MintValue> {
fn to_plutus_data(&self) -> PlutusData {
value_to_plutus_data(
self.0.mint_value.iter(),
@ -429,7 +429,7 @@ impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, Vec<Transacti
}
}
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Vec<TransactionOutput>> {
impl ToPlutusData for WithZeroAdaAsset<'_, Vec<TransactionOutput>> {
fn to_plutus_data(&self) -> PlutusData {
Data::list(
self.0
@ -465,7 +465,7 @@ impl<'a> ToPlutusData for WithOptionDatum<'a, WithZeroAdaAsset<'a, TransactionOu
}
}
impl<'a> ToPlutusData for WithZeroAdaAsset<'a, TransactionOutput> {
impl ToPlutusData for WithZeroAdaAsset<'_, TransactionOutput> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
TransactionOutput::Legacy(legacy_output) => {
@ -528,7 +528,7 @@ impl ToPlutusData for StakeCredential {
}
}
impl<'a> ToPlutusData for WithPartialCertificates<'a, Vec<Certificate>> {
impl ToPlutusData for WithPartialCertificates<'_, Vec<Certificate>> {
fn to_plutus_data(&self) -> PlutusData {
self.0
.iter()
@ -538,7 +538,7 @@ impl<'a> ToPlutusData for WithPartialCertificates<'a, Vec<Certificate>> {
}
}
impl<'a> ToPlutusData for WithPartialCertificates<'a, Certificate> {
impl ToPlutusData for WithPartialCertificates<'_, Certificate> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
Certificate::StakeRegistration(stake_credential) => {
@ -586,7 +586,7 @@ impl<'a> ToPlutusData for WithPartialCertificates<'a, Certificate> {
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, Certificate> {
impl ToPlutusData for WithNeverRegistrationDeposit<'_, Certificate> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
Certificate::StakeRegistration(stake_credential) => wrap_multiple_with_constr(
@ -870,7 +870,7 @@ impl ToPlutusData for TxInInfo {
// NOTE: This is a _small_ abuse of the 'WithWrappedTransactionId'. We know the wrapped
// is needed for V1 and V2, and it also appears that for V1 and V2, the certifying
// purpose mustn't include the certificate index. So, we also short-circuit it here.
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
impl ToPlutusData for WithWrappedTransactionId<'_, ScriptPurpose> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
@ -891,7 +891,7 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> {
}
}
impl<'a> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptPurpose> {
impl ToPlutusData for WithNeverRegistrationDeposit<'_, ScriptPurpose> {
fn to_plutus_data(&self) -> PlutusData {
match self.0 {
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
@ -1210,14 +1210,14 @@ impl ToPlutusData for RationalNumber {
}
}
impl<'a> ToPlutusData for WithArrayRational<'a, RationalNumber> {
impl ToPlutusData for WithArrayRational<'_, RationalNumber> {
fn to_plutus_data(&self) -> PlutusData {
let gcd = self.0.numerator.gcd(&self.0.denominator);
vec![self.0.numerator / gcd, self.0.denominator / gcd].to_plutus_data()
}
}
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, Vec<(Address, Coin)>> {
impl ToPlutusData for WithWrappedStakeCredential<'_, Vec<(Address, Coin)>> {
fn to_plutus_data(&self) -> PlutusData {
self.0
.iter()
@ -1232,7 +1232,7 @@ impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, Vec<(Address, Coin)>> {
}
}
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, KeyValuePairs<Address, Coin>> {
impl ToPlutusData for WithWrappedStakeCredential<'_, KeyValuePairs<Address, Coin>> {
fn to_plutus_data(&self) -> PlutusData {
KeyValuePairs::from(
self.0
@ -1249,7 +1249,7 @@ impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, KeyValuePairs<Address,
}
}
impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, StakeCredential> {
impl ToPlutusData for WithWrappedStakeCredential<'_, StakeCredential> {
fn to_plutus_data(&self) -> PlutusData {
wrap_with_constr(0, self.0.to_plutus_data())
}
@ -1291,7 +1291,7 @@ impl ToPlutusData for Vote {
}
}
impl<'a, T> ToPlutusData for WithNeverRegistrationDeposit<'a, ScriptInfo<T>>
impl<T> ToPlutusData for WithNeverRegistrationDeposit<'_, ScriptInfo<T>>
where
T: ToPlutusData,
{

View File

@ -0,0 +1,12 @@
name = "aiken-lang/acceptance_test_006"
version = "0.0.0"
compiler = "v1.1.7"
plutus = "v3"
description = ""
[[dependencies]]
name = "aiken-lang/stdlib"
version = "v2.1.0"
source = "github"
[config]

View File

@ -0,0 +1,86 @@
use aiken/collection/list
use cardano/assets.{PolicyId}
use cardano/transaction.{InlineDatum, Output, OutputReference, Transaction}
pub type StateMachineDatum {
state: Int,
buyer: ByteArray,
seller: ByteArray,
collateral: Int,
price: Int,
accept_range: Int,
}
pub type StateMachineInput {
Return
Other
}
validator statemachine(threadtoken: PolicyId) {
spend(
datum_opt: Option<StateMachineDatum>,
redeemer: StateMachineInput,
own_ref: OutputReference,
transaction: Transaction,
) {
expect Some(datum) = datum_opt
when (datum, redeemer) is {
(
StateMachineDatum {
state,
buyer,
seller,
price,
collateral,
accept_range,
},
Return,
) -> {
let must_be_state = state == 0
let must_be_signed = list.has(transaction.extra_signatories, buyer)
//One of the transaction inputs belongs to the statemachine.
expect Some(sm_input) =
list.find(
transaction.inputs,
fn(input) { input.output_reference == own_ref },
)
//One of the transaction outputs contains the threadtoken addressed to the statemachine itself - 1.
expect Some(sm_output) =
list.find(
transaction.outputs,
fn(output) { output.address == sm_input.output.address },
)
//One of the transaction outputs contains the threadtoken addressed to the statemachine itself - 2.
let must_be_policy =
list.has(assets.policies(sm_output.value), threadtoken)
//verification of the new datum - 1.
let new_data: Data =
StateMachineDatum {
state: -1,
buyer,
seller,
collateral,
price,
accept_range,
}
//verification of the new datum - 2.
let must_be_datum = InlineDatum(new_data) == sm_output.datum
and {
must_be_state?,
must_be_signed?,
must_be_policy?,
must_be_datum?,
}
}
_ -> False
}
}
else(_) {
fail
}
}