Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Aiken Language #6991

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,9 @@
[submodule "vendor/grammars/vscode-TalonScript"]
path = vendor/grammars/vscode-TalonScript
url = https://github.com/mrob95/vscode-TalonScript.git
[submodule "vendor/grammars/vscode-aiken"]
path = vendor/grammars/vscode-aiken
url = https://github.com/aiken-lang/vscode-aiken.git
[submodule "vendor/grammars/vscode-antlers-language-server"]
path = vendor/grammars/vscode-antlers-language-server
url = https://github.com/Stillat/vscode-antlers-language-server.git
Expand Down
3 changes: 3 additions & 0 deletions grammars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,9 @@ vendor/grammars/vsc-fennel:
vendor/grammars/vscode-TalonScript:
- markdown.talon.codeblock
- source.talon
vendor/grammars/vscode-aiken:
- source.aiken
- source.uplc
vendor/grammars/vscode-antlers-language-server:
- text.html.statamic
vendor/grammars/vscode-bitbake:
Expand Down
18 changes: 17 additions & 1 deletion lib/linguist/languages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ Agda:
tm_scope: source.agda
ace_mode: text
language_id: 12
Aiken:
type: programming
color: "#640ff8"
ace_mode: text
extensions:
- ".ak"
tm_scope: source.aiken
language_id: 899409497
Alloy:
type: programming
color: "#64C800"
Expand Down Expand Up @@ -7446,6 +7454,14 @@ UnrealScript:
codemirror_mode: clike
codemirror_mime_type: text/x-java
language_id: 382
Untyped Plutus Core:
type: programming
color: "#36adbd"
ace_mode: text
extensions:
- ".uplc"
tm_scope: source.uplc
language_id: 1061635506
UrWeb:
type: programming
color: "#ccccee"
Expand Down Expand Up @@ -8515,4 +8531,4 @@ xBase:
- ".prw"
tm_scope: source.harbour
ace_mode: text
language_id: 421
language_id: 421
162 changes: 162 additions & 0 deletions samples/Aiken/multi.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use aiken/builtin
use aiken/bytearray
use aiken/dict
use aiken/hash.{blake2b_256}
use aiken/list
use aiken/transaction.{
InlineDatum, Input, Output, ScriptContext, Spend, Transaction,
} as tx
use aiken/transaction/credential.{Address, PaymentCredential, ScriptCredential}
use aiken/transaction/value

type Action {
Mint(Int)
Burn
}

type SpendTokenName =
ByteArray

validator(creator: ByteArray) {
fn redeem(
// Each spend input checks for a token name matching the datum being burned
datum: SpendTokenName,
_r: Data,
ctx: ScriptContext,
) {
let ScriptContext { transaction, purpose } = ctx

let Transaction { inputs, mint, .. } = transaction

expect Spend(own_ref) = purpose

expect Some(own_input) =
list.find(inputs, fn(input) { input.output_reference == own_ref })

let Input {
output: Output { address: Address { payment_credential, .. }, .. },
..
} = own_input

expect ScriptCredential(own_validator_hash) = payment_credential

(
mint
|> value.from_minted_value
|> value.quantity_of(own_validator_hash, datum)
) == -1
}

fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool {
// get values from transaction and purpose
let ScriptContext { transaction, purpose } = ctx

expect tx.Mint(policy_id) = purpose

let Transaction { inputs, mint, extra_signatories, outputs, .. } =
transaction

let minted_assets =
mint
|> value.from_minted_value
|> value.tokens(policy_id)
|> dict.to_pairs()

when rdmr is {
Mint(total) -> {
expect [input, ..] = inputs
// Base is created from serializing a utxo ref being spent. Thus this guarantees a unique base
let base = builtin.serialise_data(input.output_reference)
// Create a list of expected token names
let expected_minted_token_names =
create_expected_minted_nfts(base, total, [])
// Check contract creator is a signer of this tx
let signature_check =
list.any(extra_signatories, fn(n) { creator == n })
// Support multiple gift card creation by allowing a
// 'number of tokens minted' == 'outputs with datum being token name'
signature_check && check_mint_and_outputs(
minted_assets,
outputs,
expected_minted_token_names,
ScriptCredential(policy_id),
)
}
Burn ->
list.all(
minted_assets,
fn(asset) {
let Pair(_, amount) = asset
amount == -1
},
)
}
}
}

fn insert(self: List<a>, e: a, compare: fn(a, a) -> Ordering) -> List<a> {
when self is {
[] ->
[e]
[x, ..xs] ->
if compare(e, x) == Less {
[e, ..self]
} else {
[x, ..insert(xs, e, compare)]
}
}
}

// Check each minted token name is in the expected list, has quantity of 1,
// and has a corresponding ouput with datum containing token name.
// Otherwise fail
fn check_mint_and_outputs(
minted_assets: Pairs<ByteArray, Int>,
outputs: List<Output>,
expected_assets: List<ByteArray>,
validator_cred: PaymentCredential,
) -> Bool {
when minted_assets is {
[] -> True
[Pair(minted_asset_name, quantity), ..rest_assets] -> {
expect
list.any(
expected_assets,
fn(expected_asset) { expected_asset == minted_asset_name },
)

expect
list.any(
outputs,
fn(output) {
let Output { address, datum, .. } = output
datum == InlineDatum(minted_asset_name) && address.payment_credential == validator_cred
},
)

quantity == 1 && check_mint_and_outputs(
rest_assets,
outputs,
expected_assets,
validator_cred,
)
}
}
}

fn create_expected_minted_nfts(
base: ByteArray,
counter: Int,
accum: List<ByteArray>,
) -> List<ByteArray> {
if counter == 0 {
accum
} else {
let token_name = blake2b_256(bytearray.push(base, counter))

let accum =
[token_name, ..accum]

create_expected_minted_nfts(base, counter - 1, accum)
}
}
50 changes: 50 additions & 0 deletions samples/Aiken/oneshot.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use aiken/dict
use aiken/list
use aiken/transaction.{OutputReference, ScriptContext, Transaction} as tx
use aiken/transaction/value

type Action {
Mint
Burn
}

validator(token_name: ByteArray, utxo_ref: OutputReference) {
fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool {
let ScriptContext { transaction, purpose } = ctx

expect tx.Mint(policy_id) = purpose

let Transaction { inputs, mint, .. } = transaction

expect [Pair(asset_name, amount)] =
mint
|> value.from_minted_value
|> value.tokens(policy_id)
|> dict.to_pairs()

when rdmr is {
Mint -> {
expect Some(_input) =
list.find(inputs, fn(input) { input.output_reference == utxo_ref })
amount == 1 && asset_name == token_name
}
Burn -> amount == -1 && asset_name == token_name
}
}
}

validator(token_name: ByteArray, policy_id: ByteArray) {
fn redeem(_d: Data, _r: Data, ctx: ScriptContext) -> Bool {
let ScriptContext { transaction, .. } = ctx

let Transaction { mint, .. } = transaction

expect [Pair(asset_name, amount)] =
mint
|> value.from_minted_value
|> value.tokens(policy_id)
|> dict.to_pairs()

amount == -1 && asset_name == token_name
}
}
Loading