From a9eb85a2f5bb06019bb63181557d6c9efb48e9dc Mon Sep 17 00:00:00 2001 From: Valdryan Ivandito Date: Tue, 22 Oct 2024 16:01:15 +0700 Subject: [PATCH] first commit --- .github/workflows/continuous-integration.yml | 18 ++++ .gitignore | 6 ++ README.md | 65 ++++++++++++++ aiken.lock | 15 ++++ aiken.toml | 18 ++++ plutus.json | 81 +++++++++++++++++ validators/hello_world.ak | 92 ++++++++++++++++++++ 7 files changed, 295 insertions(+) create mode 100644 .github/workflows/continuous-integration.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 aiken.lock create mode 100644 aiken.toml create mode 100644 plutus.json create mode 100644 validators/hello_world.ak diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..2ab4b36 --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,18 @@ +name: Continuous Integration + +on: + push: + branches: ["main"] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: aiken-lang/setup-aiken@v1 + with: + version: v1.1.4 + - run: aiken fmt --check + - run: aiken check -D + - run: aiken build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff7811b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Aiken compilation artifacts +artifacts/ +# Aiken's project working directory +build/ +# Aiken's default documentation export +docs/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a327273 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# aiken-hello-world + +Write validators in the `validators` folder, and supporting functions in the `lib` folder using `.ak` as a file extension. + +```aiken +validator my_first_validator { + spend(_datum: Option, _redeemer: Data, _output_reference: Data, _context: Data) { + True + } +} +``` + +## Building + +```sh +aiken build +``` + +## Configuring + +**aiken.toml** +```toml +[config.default] +network_id = 41 +``` + +Or, alternatively, write conditional environment modules under `env`. + +## Testing + +You can write tests in any module using the `test` keyword. For example: + +```aiken +use config + +test foo() { + config.network_id + 1 == 42 +} +``` + +To run all tests, simply do: + +```sh +aiken check +``` + +To run only tests matching the string `foo`, do: + +```sh +aiken check -m foo +``` + +## Documentation + +If you're writing a library, you might want to generate an HTML documentation for it. + +Use: + +```sh +aiken docs +``` + +## Resources + +Find more on the [Aiken's user manual](https://aiken-lang.org). diff --git a/aiken.lock b/aiken.lock new file mode 100644 index 0000000..21e7fb4 --- /dev/null +++ b/aiken.lock @@ -0,0 +1,15 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +[[requirements]] +name = "aiken-lang/stdlib" +version = "v2.1.0" +source = "github" + +[[packages]] +name = "aiken-lang/stdlib" +version = "v2.1.0" +requirements = [] +source = "github" + +[etags] diff --git a/aiken.toml b/aiken.toml new file mode 100644 index 0000000..bf7bd64 --- /dev/null +++ b/aiken.toml @@ -0,0 +1,18 @@ +name = "aiken-lang/aiken-hello-world" +version = "0.0.0" +compiler = "v1.1.4" +plutus = "v3" +license = "Apache-2.0" +description = "Aiken contracts for project 'aiken-lang/aiken-hello-world'" + +[repository] +user = "aiken-lang" +project = "aiken-hello-world" +platform = "github" + +[[dependencies]] +name = "aiken-lang/stdlib" +version = "v2.1.0" +source = "github" + +[config] diff --git a/plutus.json b/plutus.json new file mode 100644 index 0000000..4c6d577 --- /dev/null +++ b/plutus.json @@ -0,0 +1,81 @@ +{ + "preamble": { + "title": "aiken-lang/aiken-hello-world", + "description": "Aiken contracts for project 'aiken-lang/aiken-hello-world'", + "version": "0.0.0", + "plutusVersion": "v3", + "compiler": { + "name": "Aiken", + "version": "v1.1.4+79d0e45" + }, + "license": "Apache-2.0" + }, + "validators": [ + { + "title": "hello_world.hello_world.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/hello_world~1Datum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/hello_world~1Redeemer" + } + }, + "compiledCode": "59010301010032323232323225333002323232323253330073370e900118041baa0011323232533300a3370e900018059baa00513232533300f30110021533300c3370e900018069baa003132533300d3371e6eb8c044c03cdd50042450d48656c6c6f2c20576f726c642100100114a06644646600200200644a66602600229404c94ccc044cdc79bae301500200414a2266006006002602a0026eb0c040c044c044c044c044c044c044c044c044c038dd50049bae3010300e37546020601c6ea800c5858dd7180780098061baa00516300d300e002300c001300937540022c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae89", + "hash": "fdd6640d1c9a4392dd7e829f0cc4e26766539c48b2cf594959e33559" + }, + { + "title": "hello_world.hello_world.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "59010301010032323232323225333002323232323253330073370e900118041baa0011323232533300a3370e900018059baa00513232533300f30110021533300c3370e900018069baa003132533300d3371e6eb8c044c03cdd50042450d48656c6c6f2c20576f726c642100100114a06644646600200200644a66602600229404c94ccc044cdc79bae301500200414a2266006006002602a0026eb0c040c044c044c044c044c044c044c044c044c038dd50049bae3010300e37546020601c6ea800c5858dd7180780098061baa00516300d300e002300c001300937540022c6014601600460120026012004600e00260086ea8004526136565734aae7555cf2ab9f5742ae89", + "hash": "fdd6640d1c9a4392dd7e829f0cc4e26766539c48b2cf594959e33559" + } + ], + "definitions": { + "ByteArray": { + "dataType": "bytes" + }, + "VerificationKeyHash": { + "title": "VerificationKeyHash", + "dataType": "bytes" + }, + "hello_world/Datum": { + "title": "Datum", + "anyOf": [ + { + "title": "Datum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "owner", + "$ref": "#/definitions/VerificationKeyHash" + } + ] + } + ] + }, + "hello_world/Redeemer": { + "title": "Redeemer", + "anyOf": [ + { + "title": "Redeemer", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "msg", + "$ref": "#/definitions/ByteArray" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/validators/hello_world.ak b/validators/hello_world.ak new file mode 100644 index 0000000..c4ec7f0 --- /dev/null +++ b/validators/hello_world.ak @@ -0,0 +1,92 @@ +// Library standar yang digunakan +use aiken/collection/list +use aiken/crypto.{VerificationKeyHash} +use aiken/primitive/string +use cardano/transaction.{OutputReference, Transaction} + +// Mendefinisikan parameter datum berupa VerificationKeyHash dengan nama owner +// Datum menyimpan kunci verifikasi pemilik wallet +pub type Datum { + owner: VerificationKeyHash, +} + +// Mendefinisikan parameter redeemer berupa ByteArray dengan nama msg +// Redeemer menyimpan pesan +pub type Redeemer { + msg: ByteArray, +} + +// Validator hello_world merupakan fungsi utama +validator hello_world { + // Fungsi spend memvalidasi dua kondisi : + // redeemer harus "hello, world!" dan pemilik aset (di datum) harus menyetujui transaksi + spend( + datum: Option, + redeemer: Redeemer, + _own_ref: OutputReference, + tx: Transaction, + ) { + // Menampilkan isi dari redeemer di terminal ketika sesi testing + trace @"redeemer": string.from_bytearray(redeemer.msg) + // Konsep destructuring dan pattern matching dibutuhkan untuk memahami syntax ini + // Pada pattern matching terdapat Some dan None + // Jika parameter datum kosong maka hasilnya None + // Transaksi dibatalkan jika hasilnya None + expect Some(Datum { owner }) = datum + // Bernilai true jika pesan atau isi redeemer adalah "Hello, World!" + let must_say_hello = redeemer.msg == "Hello, World!" + // Bernilai true jika pemilik aset (di datum) menyetujui transaksi + let must_be_sign = list.has(tx.extra_signatories, owner) + // Operator ? digunakan untuk menampilkan dan menunjukan suatu parameter yang bernilai false + must_say_hello? && must_be_sign? + } + + // Jika validasi gagal karena kondisi tidak terpenuhi maka transaksi dibatalkan + else(_) { + fail + } +} + +// Skenario_1 : Pesan atau isi Redeemer tidak valid dan transaksi tidak disetujui user +test scenario_1() { + let datum = + Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } + let redeemer = Redeemer { msg: "Aiken Rocks!" } + let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } + hello_world.spend( + Some(datum), + redeemer, + placeholder_utxo, + transaction.placeholder, + ) +} + +// Skenario_2 : Pesan atau isi Redeemer valid dan transaksi tidak disetujui user +test scenario_2() { + let datum = + Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } + let redeemer = Redeemer { msg: "Hello, World!" } + let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } + hello_world.spend( + Some(datum), + redeemer, + placeholder_utxo, + transaction.placeholder, + ) +} + +// Skenario_3 : Pesan atau isi Redeemer valid dan transaksi disetujui user +test scenario_3() { + let datum = + Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } + let redeemer = Redeemer { msg: "Hello, World!" } + let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } + hello_world.spend( + Some(datum), + redeemer, + placeholder_utxo, + Transaction { ..transaction.placeholder, extra_signatories: [datum.owner] }, + ) +} + +