aiken-lang / aiken

A modern smart contract platform for Cardano
https://aiken-lang.org
Apache License 2.0
486 stars 96 forks source link

Whoops! You found a bug in the Aiken compiler. #1068

Open rey-sudo opened 3 days ago

rey-sudo commented 3 days ago

What Git revision are you using?

v2.1.0

What operating system are you using, and which version?

Describe what the problem is?


ERROR

Compiling aiken-lang/contracts 0.0.0 (.) Compiling aiken-lang/stdlib v2.1.0 (./build/packages/aiken-lang-stdlib)

aiken::fatal::error Whoops! You found a bug in the Aiken compiler.

Please report this error at https://github.com/aiken-lang/aiken/issues/new. In your bug report please provide the information below and if possible the code that produced it.

Operating System: linux Architecture: x86_64 Version: v1.1.7+e2fb28b

crates/aiken-lang/src/gen_uplc/decision_tree.rs:659:9

   assertion failed: matrix.rows.iter().all(|row| { row.columns.len() == column_length })

IMPORTS

use aiken/collection/list use cardano/transaction.{OutputReference, Transaction, NoDatum, Input, InlineDatum, Output} use cardano/assets.{PolicyId} use cardano/assets use aiken/collection/dict use aiken/crypto.{VerificationKeyHash} use cardano/address.{Address, Script} use aiken/interval


CODE

validator statemachine(threadtoken: PolicyId) {
  spend(
    datum_opt: Option<StateMachineDatum>,
    redeemer: StateMachineInput,
    own_ref: OutputReference,
    transaction: Transaction,
  ) -> Bool {
        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: buyer,
              seller : seller,
              collateral : collateral,
              price: price,
              accept_range: 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
        }
  }
}

TEST:

test cancel() {
  let own_ref =
    OutputReference {
      transaction_id: #"ee220ba9258ef159bb44f0d3522b840a80475eac2df7176f2c513d59a2ead71f",
      output_index: 0,
    }
  let threadtoken = #"99999999999999999999999999999999999999999999999999999999"
  let token_name = #"02"
  let own_val_hash = #"88888888888888888888888888888888888888888888888888888888" // self statemachine hash
  let seller_pubkeyhash = #"00000000000000000000000000000000000000000000000000000000"
  let buyer_pubkeyhash = #"77777777777777777777777777777777777777777777777777777777"  
  let accept_range = 3
  /////////////INPUT/////////////
  let input_utxo =
    Input {
      output_reference: own_ref,
      output: Output {
        address: Address {
          payment_credential: Script(own_val_hash),
          stake_credential: None,
        },
        value: assets.add(
          assets.from_lovelace(25_000_000),
          threadtoken,
          token_name,
          1,
        ),
        datum: NoDatum,
        reference_script: None,
      },
    }
  /////////////INPUT/////////////  
  let new_data: Data = StateMachineDatum {
      state : -1,
      buyer: buyer_pubkeyhash,
      seller : seller_pubkeyhash,
      collateral : 25000000,
      price: 50000000,
      accept_range: accept_range
  }  

  let new_datum = InlineDatum(new_data)

  let output =
    Output {
      address: Address {
        payment_credential: Script(own_val_hash),
        stake_credential: None,
      },
      value: assets.add(
        assets.from_lovelace(25_000_000),
        threadtoken,
        token_name,
        1,
      ),
      datum: new_datum,
      reference_script: None,
    }

  let redeemer = Return       //StateMachineInput

  let old_datum =
    StateMachineDatum {
      state: 0,
      buyer: buyer_pubkeyhash,
      seller: seller_pubkeyhash,
      collateral: 25000000,
      price: 50000000,
      accept_range: accept_range
    }

  let transaction =
    Transaction {
      inputs: [input_utxo],
      reference_inputs: [],
      outputs: [output],
      fee: 0,
      mint: transaction.placeholder.mint,
      certificates: [],
      withdrawals: [],
      validity_range: interval.after(accept_range),
      extra_signatories: [old_datum.buyer],
      redeemers: [],
      datums: transaction.placeholder.datums,
      id: transaction.placeholder.id,
      votes: transaction.placeholder.votes,
      proposal_procedures: transaction.placeholder.proposal_procedures,
      current_treasury_amount: transaction.placeholder.current_treasury_amount,
      treasury_donation: transaction.placeholder.treasury_donation
    }

  statemachine.spend(threadtoken, Some(old_datum), redeemer, own_ref, transaction)
}

What should be the expected behavior?

Some kind of error is not displayed. The error is not coming from the validator, since without the cancel() test it works. The cancel() test has worked before when plutus was V2. But this time it throws an unknown error or bug.

rey-sudo commented 3 days ago

if you are going to use pattern matching make sure to use at least 2 options without _ -> False.