On-chain code using Aiken

If you have not installed Aiken on your computer refer to Aiken installation guide.

Let's create a new Aiken project

Open the command prompt then run this:

aiken new aiken-lang/matching
cd matching
code.// open VS code 

You can see a bunch of files in the folder. Now we want to create our validator. We are going to do that in our validators folder. So go to that folder and create a new file called “matching.ak”. A validator is where we are going to verify our transaction. We are going to verify our signature and the transaction data in there.

Before we create our validator function, we need to define our custom types which it is going to be our Datum and Redeemer.

What is a datum?

A datum is a piece of data attached to the transaction output that contains relevant logic of the smart contract. A datum is set when locking funds in the smart contract. In this example, our datum will contain the owner verificationKeyHash, beneficiary verificationKeyHash, and password. The verificationKeyHash will be used to require a signature from the owner or beneficiary when unlocking the funds. The password set when locking the funds would have to match with the password of the redeemer when unlocking the funds.

type Datum {
owner: VerificationKeyHash,
beneficary: VerificationKeyHash,
password: ByteArray,
}
type VerificationKeyHash =
Hash<Blake2b_224, VerificationKey>

What is a redeemer?

A redeemer contains all the logic necessary to verify the transaction. The redeemer will be used to unlock the funds. The redeemer password has to match with the password set in the datum in this case.

type Redeemer {
msg
}

Let's go ahead and define our validator

validator {
fn match(datum: Datum, redeemer: Redeemer, context: ScriptContext) -> Bool {
when context.purpose is {
Spend(_) -> or {
list.has(context.transaction.extra_signatories, datum.owner),
and {
redeemer.msg == datum.password,
list.has(context.transaction.extra_signatories, datum.beneficary),
},
}
_ -> F
  }
 }
}

Notice that we have ScriptContext in our validator.

ScriptContext refers to the information available when validating a smart contract on a blockchain. It has two fields, ScriptPurpose and ScriptTransaction. ScriptPurpose is where you get the script purpose, which is divided into spending, minting, Rewarding StakingCredentail, etc. In this case, we are using spend because we just spend some funds. ScriptTransaction contains all information about the transaction, such as the transaction output, inputs, and signatures.

We did not import our dependencies at the top, let's go ahead and do that. So, there is the full code containing our dependencies.

use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{ScriptContext, Spend}
use aiken/transaction/credential.{VerificationKey}
type Datum {
owner: VerificationKeyHash,
beneficary: VerificationKeyHash,
password: ByteArray,
}
type Redeemer {
    msg: ByteArray,
}
type VerificationKeyHash =
Hash<Blake2b_224, VerificationKey>
validator {
fn match(datum: Datum, redeemer: Redeemer, context: ScriptContext) -> Bool {
when context.purpose is {
Spend(_) -> or {
list.has(context.transaction.extra_signatories, datum.owner),
and {
redeemer.msg == datum.password,
list.has(context.transaction.extra_signatories, datum.beneficary),
},
}
_ -> False
}
}
}

Once that is done we can go ahead and run Aiken check in the command prompt which will check for errors.

If there are no errors, we can run Aiken build which will generate a file called “plutus.json”. We will use this file in the off-chain code so that we can interact with the on-chain code.

source code


Written by: Banele Nhlapo
email address: nhlapob78@gmail.com