Skip to content

Commit

Permalink
Merge pull request #1 from Anastasia-Labs/linked-list
Browse files Browse the repository at this point in the history
Add linked list lib
  • Loading branch information
solidsnakedev authored Jan 16, 2024
2 parents dee719e + f8edf81 commit 54e72cb
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 1 deletion.
21 changes: 21 additions & 0 deletions .github/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: aiken-lang/[email protected]
with:
version: v1.0.21-alpha

- run: aiken fmt --check
- run: aiken check
- run: aiken build
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
build
.VSCodeCounter
plutus.json
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
# aiken-linked-list
# aiken-linked-list

## Building

```sh
aiken build
```

To run all tests, simply do:

```sh
aiken check
```
15 changes: 15 additions & 0 deletions aiken.lock
Original file line number Diff line number Diff line change
@@ -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 = "1.7.0"
source = "github"

[[packages]]
name = "aiken-lang/stdlib"
version = "1.7.0"
requirements = []
source = "github"

[etags]
14 changes: 14 additions & 0 deletions aiken.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name = "aiken-lang/aiken-linked-list"
version = "0.0.0"
license = "Apache-2.0"
description = "Aiken contracts for project 'aiken-lang/aiken-linked-list'"

[repository]
user = "aiken-lang"
project = "aiken-linked-list"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "1.7.0"
source = "github"
1 change: 1 addition & 0 deletions lib/linkedlist/constants.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const origin_node_token_name = "FSN"
110 changes: 110 additions & 0 deletions lib/linkedlist/linked_list.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use aiken/bytearray
use aiken/interval
use aiken/list
use aiken/transaction.{Output}
use aiken/transaction/value.{lovelace_of}
use linkedlist/constants
use linkedlist/types.{Common, Config, POSIXTime, PubKeyHash, SetNode}
use linkedlist/utils

pub fn init(common: Common) -> Bool {
let must_spend_nodes = list.length(common.node_inputs) > 0
let must_exactly_one_node_output = list.length(common.node_outputs) == 1
let must_mint_correctly =
utils.validate_mint(
common.mint,
common.own_cs,
constants.origin_node_token_name,
1,
)
must_spend_nodes? && must_exactly_one_node_output? && must_mint_correctly?
}

pub fn deinit(common: Common) -> Bool {
let must_spend_exactly_one_node_input = list.length(common.node_inputs) == 1
let must_not_produce_node_output = list.length(common.node_outputs) == 0
let must_burn_correctly =
utils.validate_mint(
common.mint,
common.own_cs,
constants.origin_node_token_name,
-1,
)
must_spend_exactly_one_node_input? && must_not_produce_node_output? && must_burn_correctly?
}

pub fn insert(common: Common, insert_key: PubKeyHash, node: SetNode) -> Bool {
let must_cover_inserting_key = utils.cover_key(node, insert_key)
expect [covering_node] = common.node_inputs
let prev_node_datum = utils.as_predecessor_of(node, insert_key)
let node_datum = utils.as_successor_of(insert_key, node)
let must_has_datum_in_output =
list.any(
common.node_outputs,
fn(node_pair) { node_datum == node_pair.node },
)
let must_correct_node_output =
list.any(
common.node_outputs,
fn(node_pair) {
covering_node.val == node_pair.val && prev_node_datum == node_pair.node
},
)

let must_mint_correct =
utils.validate_mint(
common.mint,
common.own_cs,
bytearray.concat(constants.origin_node_token_name, insert_key),
1,
)
must_cover_inserting_key? && must_has_datum_in_output? && must_correct_node_output? && must_mint_correct?
}

pub fn remove(
common: Common,
range: POSIXTime,
disc_config: Config,
outs: List<Output>,
sigs: List<PubKeyHash>,
remove_key: PubKeyHash,
node: SetNode,
) -> Bool {
let must_cover_remove_key = utils.cover_key(node, remove_key)
let prev_node_datum = utils.as_predecessor_of(node, remove_key)
let node_datum = utils.as_successor_of(remove_key, node)
let must_spend_two_nodes = list.length(common.node_inputs) == 2
expect Some(stay_node) =
list.find(common.node_inputs, fn(input) { prev_node_datum == input.node })
expect Some(remove_node) =
list.find(common.node_inputs, fn(input) { node_datum == input.node })
let remove_token_name =
bytearray.concat(constants.origin_node_token_name, remove_key)
let must_correct_node_output =
list.any(
common.node_outputs,
fn(node_pair) { stay_node.val == node_pair.val && node == node_pair.node },
)
let must_mint_correct =
utils.validate_mint(common.mint, common.own_cs, remove_token_name, -1)
let must_sign_by_user = list.has(sigs, remove_key)
let own_input_lovelace = lovelace_of(remove_node.val)
let own_input_fee = utils.div_ceil(own_input_lovelace, 4)
let disc_deadline = disc_config.deadline
let must_satisfy_removal_broke_phase_rules =
if
interval.is_entirely_after(interval.after(disc_deadline - 8_640_000), range){

True
} else {
list.any(
outs,
fn(out) {
out.address == disc_config.penalty_address && own_input_fee < lovelace_of(
out.value,
)
},
)
}
must_cover_remove_key? && must_spend_two_nodes? && must_correct_node_output? && must_mint_correct? && must_sign_by_user? && must_satisfy_removal_broke_phase_rules?
}
25 changes: 25 additions & 0 deletions lib/linkedlist/tests.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use linkedlist/utils.{div_ceil}

// Test Case 1
// Test when a is divisible by b
test div_ceil_1() {
div_ceil(10, 5) == 2
}

// Test Case 2
// Test when a is not divisible by b
test div_ceil_2() {
div_ceil(7, 3) == 3
}

// Test Case 3
// Test when a is 0
test div_ceil_3() {
div_ceil(0, 5) == 0
}

// Test Case 4
// Test when b is 1
test div_ceil_4() {
div_ceil(10, 1) == 10
}
51 changes: 51 additions & 0 deletions lib/linkedlist/types.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use aiken/hash.{Blake2b_224, Hash}
use aiken/transaction.{OutputReference}
use aiken/transaction/credential.{Address, VerificationKey}
use aiken/transaction/value.{AssetName, PolicyId, Value}

/// A number of milliseconds since 00:00:00 UTC on 1 January 1970.
pub type POSIXTime =
Int

pub type AssetClass {
policy_id: PolicyId,
asset_name: AssetName,
}

pub type PubKeyHash =
Hash<Blake2b_224, VerificationKey>

pub type Config {
init_utxo: OutputReference,
deadline: POSIXTime,
penalty_address: Address,
}

pub type NodeKey {
Key { key: PubKeyHash }
Empty
}

pub type SetNode {
key: NodeKey,
next: NodeKey,
}

pub type NodePair {
val: Value,
node: SetNode,
}

pub type Common {
own_cs: PolicyId,
mint: Value,
node_inputs: List<NodePair>,
node_outputs: List<NodePair>,
}

pub type NodeAction {
Init
Deinit
Insert
Remove
}
45 changes: 45 additions & 0 deletions lib/linkedlist/utils.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use aiken/bytearray
use aiken/dict
use aiken/transaction/value.{Value}
use linkedlist/types.{Empty, Key, PubKeyHash, SetNode}

pub fn validate_mint(
mints: Value,
expected_minting_policy: ByteArray,
expected_minting_name: ByteArray,
expected_minting_amt: Int,
) -> Bool {
let mints_policy = dict.to_list(value.tokens(mints, expected_minting_policy))
mints_policy == [(expected_minting_name, expected_minting_amt)]
}

pub fn cover_key(node: SetNode, insert_key: PubKeyHash) -> Bool {
let less_than_key =
when node.key is {
Empty -> True
Key(key) -> bytearray.compare(key, insert_key) == Less
}
let more_than_key =
when node.next is {
Empty -> True
Key(key) -> bytearray.compare(key, insert_key) == Greater
}
less_than_key? && more_than_key?
}

pub fn as_predecessor_of(node: SetNode, next_key: PubKeyHash) -> SetNode {
SetNode { key: node.key, next: Key(next_key) }
}

pub fn as_successor_of(prev_key: PubKeyHash, node: SetNode) -> SetNode {
SetNode { key: Key(prev_key), next: node.next }
}

pub fn div_ceil(a, b: Int) -> Int {
let div = a / b
let rem = a % b
when rem is {
0 -> div
_ -> div + 1
}
}

0 comments on commit 54e72cb

Please sign in to comment.