Skip to content

Commit

Permalink
feat: implement 1st pass of space-time contract
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega committed Feb 1, 2024
1 parent 57e95eb commit 98ca5aa
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Tests

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

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

- uses: aiken-lang/setup-aiken@v0.1.0
with:
version: v1.0.24-alpha

- run: aiken fmt --check
- run: aiken check -D
- run: aiken build
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Aiken compilation artifacts
artifacts/
# Aiken's project working directory
build/
# Aiken's default documentation export
docs/
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 = "txpipe/asteria"
version = "0.0.0"
license = "Apache-2.0"
description = "Aiken contracts for project 'txpipe/asteria'"

[repository]
user = "txpipe"
project = "asteria"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "1.7.0"
source = "github"
108 changes: 108 additions & 0 deletions plutus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"preamble": {
"title": "txpipe/asteria",
"description": "Aiken contracts for project 'txpipe/asteria'",
"version": "0.0.0",
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.24-alpha+982eff4"
},
"license": "Apache-2.0"
},
"validators": [
{
"title": "spacetime.spend",
"datum": {
"title": "datum",
"schema": {
"$ref": "#/definitions/spacetime~1Datum"
}
},
"redeemer": {
"title": "redeemer",
"schema": {
"$ref": "#/definitions/spacetime~1Redeemer"
}
},
"parameters": [
{
"title": "ship_policy_id",
"schema": {
"$ref": "#/definitions/ByteArray"
}
},
{
"title": "max_speed",
"schema": {
"$ref": "#/definitions/Int"
}
},
{
"title": "fuel_per_step",
"schema": {
"$ref": "#/definitions/Int"
}
}
],
"compiledCode": "59035c010000323232323232323232232232232222323232533300f32323232323232323232323232323232323232323253330243370e9001181180309919191919299981499b8748000c0a00044c8c8c8c8c94ccc0b8cdc3a4000605a00226464646464646464a66606c66e1d2004303500113232323232533303b533303b533303b533303b00b100914a020102940400852808008a503371e6eb8c050c0e000c0994ccc0e4cdc39bad300e303700233700046052266e1cdd69802981b80119b8002102714a06058002607800260680022c6002606600c460746076607600266e24cdc101580100c99b8900102c3233700600204460020404a66606466e2000520001337029000000880099b873330043756600a605c600a605c00c0580389001181a00098160008b19803005119b873330023756600660580020540349001111191919299981919b8748008004520001375a606e6060004606000264a66606266e1d200200114c0103d87a8000132323300100100222533303700114c103d87a800013232323253330383371e014004266e9520003303c375000297ae0133006006003375a60720066eb8c0dc008c0ec008c0e4004dd5981b18178011817800991980080080211299981a0008a6103d87a800013232323253330353371e010004266e95200033039374c00297ae01330060060033756606c0066eb8c0d0008c0e0008c0d80048c0c4c0c8004c0bc004c09c00458cc0040208cdd7980198138008021119198008008019129998170008a6103d87a800013232533302d300500213374a90001981880125eb804cc010010004c0c8008c0c00048c0b0004c0a8004c08801858dd61814000981400098138011bac3025001301d0033023001302300230210013019010375a603e002603e0046eb4c074004c074008dd6980d800980d8011bae3019001301100a375a602e002602e0046eb4c054004c0340145261365632533300f3370e9000000899191919299980b180c8010a4c2c6eb4c05c004c05c008dd6980a80098068028b18068021800802119299980719b87480000044c8c8c8c8c8c8c8c94ccc064c07000852616375a603400260340046eb4c060004c060008dd6980b000980b0011bae3014001300c00216300c001375a0026eb4004dd7000918029baa001230033754002ae6955ceaab9e5573eae815d0aba21",
"hash": "4169f9c7110ee486b5e1872d123b10aa4924f38c8a47291b5d8e3422"
}
],
"definitions": {
"ByteArray": {
"dataType": "bytes"
},
"Int": {
"dataType": "integer"
},
"spacetime/Datum": {
"title": "Datum",
"anyOf": [
{
"title": "Datum",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "ship_id",
"$ref": "#/definitions/ByteArray"
},
{
"title": "pos_x",
"$ref": "#/definitions/Int"
},
{
"title": "pos_y",
"$ref": "#/definitions/Int"
},
{
"title": "fuel",
"$ref": "#/definitions/Int"
}
]
}
]
},
"spacetime/Redeemer": {
"title": "Redeemer",
"anyOf": [
{
"title": "Redeemer",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "delta_x",
"$ref": "#/definitions/Int"
},
{
"title": "delta_y",
"$ref": "#/definitions/Int"
}
]
}
]
}
}
}
71 changes: 71 additions & 0 deletions validators/spacetime.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use aiken/list
use aiken/math
use aiken/transaction.{InlineDatum, ScriptContext, Spend, Transaction}
use aiken/transaction/value.{PolicyId, quantity_of}

type Datum {
ship_id: ByteArray,
pos_x: Int,
pos_y: Int,
fuel: Int,
}

type Redeemer {
delta_x: Int,
delta_y: Int,
}

fn distance(delta_x: Int, delta_y: Int) -> Int {
math.abs(delta_x) + math.abs(delta_y)
}

// we need to enforce:
// [x] that the speed (manhattan_distance/slot) doesn't exceed an upper bound
// [ ] that the ship has enough fuel (token) to move the desired delta
// [ ] that the new position of the ship utxo equals the last position + delta
// [ ] that the fuel required for the delta has been burned
// [ ] that the tx is signed by the owner of the ship (token holder)

validator(ship_policy_id: PolicyId, max_speed: Int, fuel_per_step: Int) {
fn spend(datum: Datum, redeemer: Redeemer, ctx: ScriptContext) -> Bool {
let Redeemer { delta_x, delta_y } = redeemer

let Datum { ship_id, pos_x, pos_y, fuel } = datum

let ScriptContext { transaction, purpose } = ctx

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

expect Spend(utxo_ref) = purpose

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

expect Some(own_output) =
list.find(
outputs,
fn(output) { quantity_of(output.value, ship_policy_id, ship_id) == 1 },
)

let must_hold_ship_asset =
quantity_of(own_input.output.value, ship_policy_id, ship_id) == 1

let distance = distance(delta_x, delta_y)

// speed = distance since we consider each block the unit of time
let must_respect_max_speed = distance <= max_speed

let must_have_enough_fuel = fuel >= fuel_per_step * distance

expect InlineDatum(out_data) = own_output.datum

expect out_data: Datum = out_data

let final_pos_must_respect_movement =
out_data.pos_x == pos_x + delta_x && out_data.pos_y == pos_y + delta_y

let final_ship_id_must_match = out_data.ship_id == ship_id

must_hold_ship_asset && must_respect_max_speed && must_have_enough_fuel && final_pos_must_respect_movement && final_ship_id_must_match
}
}

0 comments on commit 98ca5aa

Please sign in to comment.