From 7f6a4c3a4405ab0894d01e63677e4240ee107b71 Mon Sep 17 00:00:00 2001 From: Francisco <121896075+franciscojoray@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:43:24 -0300 Subject: [PATCH] 15 implement shipyardpolicy (#23) * implemented shipyard policy. * cleaned pellet code. * fixed shipyard policy. * renamed pellet tests. * shipyard policy tests. * burn tests. --- onchain/docs/mvp-design/design.md | 2 +- onchain/src/lib/asteria/types.ak | 4 +- onchain/src/lib/asteria/utils.ak | 28 +- onchain/src/plutus.json | 40 +- onchain/src/validators/pellet.ak | 16 +- onchain/src/validators/shipyard.ak | 103 ++++- .../tests/{pellet_tests.ak => pellet.ak} | 46 +- onchain/src/validators/tests/shipyard.ak | 413 ++++++++++++++++++ 8 files changed, 588 insertions(+), 64 deletions(-) rename onchain/src/validators/tests/{pellet_tests.ak => pellet.ak} (89%) create mode 100644 onchain/src/validators/tests/shipyard.ak diff --git a/onchain/docs/mvp-design/design.md b/onchain/docs/mvp-design/design.md index 51350bc..2814fa9 100644 --- a/onchain/docs/mvp-design/design.md +++ b/onchain/docs/mvp-design/design.md @@ -162,7 +162,7 @@ Pays the min ada locked in the `ShipState` UTxO back to the ship owner and burns * no `ShipState` output. ### Ship minting policy or "ShipyardPolicy": -* Params: `SpaceTimeScript` validator address and Asteria validator address. +* Params: `SpaceTimeScript` validator address, Asteria validator address, ship's initial fuel and minimum initial distance from Asteria. #### MINT: * `AsteriaUTxO` is input. diff --git a/onchain/src/lib/asteria/types.ak b/onchain/src/lib/asteria/types.ak index 11efd8a..4befe4f 100644 --- a/onchain/src/lib/asteria/types.ak +++ b/onchain/src/lib/asteria/types.ak @@ -43,6 +43,6 @@ pub type AsteriaRedeemer { } pub type ShipyardRedeemer { - Mint - Burn + MintShip + BurnShip } diff --git a/onchain/src/lib/asteria/utils.ak b/onchain/src/lib/asteria/utils.ak index 5757e6a..3df57a7 100644 --- a/onchain/src/lib/asteria/utils.ak +++ b/onchain/src/lib/asteria/utils.ak @@ -1,19 +1,19 @@ use aiken/bytearray.{length, take} use aiken/dict.{keys} use aiken/list.{any} -use aiken/transaction.{Input} -use aiken/transaction/value.{PolicyId, tokens} +use aiken/math +use aiken/transaction.{Output} +use aiken/transaction/value.{AssetName, PolicyId, tokens} -pub fn is_ship_token_input( - inputs: List, - shipyard_policy: PolicyId, -) -> Bool { - let ship_name = "SHIP" - any( - inputs, - fn(input) { - let token_names = keys(tokens(input.output.value, shipyard_policy)) - any(token_names, fn(name) { take(name, length(ship_name)) == ship_name }) - }, - ) +pub fn is_ship_token_in_utxo(utxo: Output, shipyard_policy: PolicyId) -> Bool { + let token_names = keys(tokens(utxo.value, shipyard_policy)) + any(token_names, fn(name) { has_prefix("SHIP", name) }) +} + +pub fn has_prefix(prefix: ByteArray, name: AssetName) -> Bool { + take(name, length(prefix)) == prefix +} + +pub fn distance(delta_x: Int, delta_y: Int) -> Int { + math.abs(delta_x) + math.abs(delta_y) } diff --git a/onchain/src/plutus.json b/onchain/src/plutus.json index b247962..46f79e1 100644 --- a/onchain/src/plutus.json +++ b/onchain/src/plutus.json @@ -6,7 +6,7 @@ "plutusVersion": "v2", "compiler": { "name": "Aiken", - "version": "v1.0.24-alpha+982eff4" + "version": "v1.0.26-alpha+075668b" }, "license": "Apache-2.0" }, @@ -33,8 +33,8 @@ } } ], - "compiledCode": "582801000032323223222253330064a229309b2b1919bb03004001300430050013758002ae695d0aba21", - "hash": "631aff3dfb3ee15c0a560797a3e575872b27afe17662be117974aa0a" + "compiledCode": "5898010000323232323232232232232253330084a229309b2b19299980399b874800000454ccc028c024dd50010a4c2c2a66600e66e1d20020011533300a300937540042930b0b18039baa00153330043370e900018029baa001132323232533300b300d002149858dd7180580098058011bad3009001300637540022c6466ec0c01c004c01cc020004dd6000ab9a5573aaae7955cfaba157441", + "hash": "27cbc8819bede902bc58445bb6fc3d8c91341d0f48f58b5ea7adb72e" }, { "title": "pellet.spend", @@ -58,33 +58,45 @@ } } ], - "compiledCode": "5903b00100003232323232323232322222323232533300a3232323232323232323232323232323232533301b3370e9001180d0030991919191919299981218138010991919191929998149816001099299981399b8748010c0980044c8c8c8c8c8c8c94ccc0b94ccc0b94ccc0b94ccc0b8010400c52808010a50100114a026464646600200203444a66606a00229404c8c94ccc0d0c8c8cc004004008894ccc0e800452809919299981c99b8f33371890001b8d48904534849500000248904534849500014a2266008008002607c0046eb8c0f0004c014c8c8c94ccc0dccdc3a4004002297adef6c60137566078606a004606a002660260020486eacc04cc0c8c04cc0c8008528899802002000981c801181b80098008009129998198008a5eb804cc0d0c0c4c0d4004cc008008c0d800452819baf0053374a9000198189ba8337020400446066605605266ebcc02cc0a8c02cc0a8030c02cc0a801ccdc39998041bab300a3029300a302900b375c601c60520506eb8c028c0a40a120023371203e03a6044002605a002604a0022c60566058605860480022c60540026600c014466e1cccc008dd5980218118009bae30083023022375c600860460449001111191919299981499b8748008004520001375a605c604e004604e00264a66605066e1d200200114c0103d87a8000132323300100100222533302e00114c103d87a8000132323232533302f3371e014004266e95200033033375000297ae0133006006003375a60600066eb8c0b8008c0c8008c0c0004dd5981698130011813000998020018011119198008008019129998148008a60103d87a8000132323232533302a3371e00e004266e9520003302e374c00297ae0133006006003375660560066eb8c0a4008c0b4008c0ac0048c09cc0a000458c094004cc0040208cdd79801980f0008021119198008008019129998128008a5eb804c8c94ccc090c0140084cc0a0008cc0100100044cc010010004c0a4008c09c0048c08c004c084004c06401858dd6180f800980f800980f0011bac301c0013014003301a001301a0023018001301000c375c602c002602c002602a00260280046eb4c048004c028020dd6980800098040028a4c26cac64a66601466e1d200000113232533300f3012002149858dd6980800098040028b18040021800802119299980499b87480000044c8c8c8c8c8c8c8c94ccc050c05c00852616375c602a002602a0046eb4c04c004c04c008dd6980880098088011bad300f0013007002163007001230053754002460066ea80055cd2ab9d5573caae7d5d02ba15745", - "hash": "58a25f995531669ea4fb67f420a94eb4c0e1575dc93e6663e5a7c274" + "compiledCode": "5903ab01000032323232323232223232322322533300932323232323232323253323301330013014375400a26464646464a666036603c0042646464646464a6660426048004264a66603e66e1d20043020375400226464a666042603c60446ea8c8cc004004048894ccc098004530103d87a800013232533302532323300100133002002325333028301630293754002297adef6c6013756605a60546ea8004cc034dd5980798149baa300f3029375400803644a66605600229404c94ccc0a4cdc7999b8c48000dc6a45045348495000375c605c00491104534849500014a2266006006002605c00244a666054002297ae013302b3028302c00133002002302d0011300c330290024bd70099802002000981500118140008991919299981219b8901b0191533302400315333024002100114a0294052819baf004300a33027375066e04060068c0a4c094dd501099baf300a30243754601460486ea802cc028c090dd500298079998031bab300930233754601260466ea8028dd7180698119baa020375c601260466ea808058c070004c090c084dd50008b18119812181218101baa0011630220013300700b2300b3330023756600a603e6ea8004dd71804980f9baa01c375c600a603e6ea8070888c94ccc07cc034c080dd50008a400026eb4c090c084dd500099299980f980698101baa00114c103d87a8000132330010013756604a60446ea8008894ccc090004530103d87a8000132323253330243371e00e6eb8c09400c4c02ccc0a0dd4000a5eb804cc014014008dd6981280118140011813000998020018011119198008008019129998108008a60103d87a8000132323253330213371e00c6eb8c08800c4c020cc094dd3000a5eb804cc014014008dd59811001181280118118009ba5480008c078c07c00458c070004cc0040188cdd79801980c9baa00100422323300100100322533301c00114bd7009919299980d980280109980f801198020020008998020020009810001180f0009180d000980c180a9baa005370e90010b1bac3016301730170023758602a00260226ea8c050008c04cc050004c03cdd50029bae3011301230123012002375a602000260186ea8020dd6980718059baa00314984d9594ccc01cc010c020dd500089919299980618078010a4c2c6eb4c034004c024dd50008b180080192999802980118031baa0011323232323232323253330103013002149858dd7180880098088011bad300f001300f002375a601a002601a0046eb4c02c004c01cdd50008b1b87480015cd2ab9d5573caae7d5d02ba157441", + "hash": "991579c92fe22adfa6a23a9e3b21f5c898fdde927f5fc27ee1f3a1da" }, { "title": "shipyard.mint", "redeemer": { - "title": "_redeemer", + "title": "redeemer", "schema": { "$ref": "#/definitions/asteria~1types~1ShipyardRedeemer" } }, "parameters": [ { - "title": "_asteria_validator_address", + "title": "asteria_validator_address", "schema": { "$ref": "#/definitions/aiken~1transaction~1credential~1Address" } }, { - "title": "_spacetime_validator_address", + "title": "spacetime_validator_address", "schema": { "$ref": "#/definitions/aiken~1transaction~1credential~1Address" } + }, + { + "title": "initial_fuel", + "schema": { + "$ref": "#/definitions/Int" + } + }, + { + "title": "min_distance", + "schema": { + "$ref": "#/definitions/Int" + } } ], - "compiledCode": "5201000032222253330054a229309b2b2b9a01", - "hash": "6586e490115868362eb1a7305c802c314357f2ebc5887f8bc4833732" + "compiledCode": "59088b01000032323232323232322223223223232322533300c323232323232325333013300b3014375400c2646464646464646464a6466603a602a603c6ea80584c94ccc078c058c07cdd5000899299980f980198101baa00113232323232325333028302b0021325333026301d375a605000426464a666056605c004264a66605260406eb4c0ac0084c8c94ccc0b8c0c40084c94ccc0b0c040c0b4dd5000899191919191919191919191919191919191919192999820181219918008009129998228008a4000266e01200233002002304800102a15333040008153330400071533304000615333040005153330400041533304000315333040002100114a029405280a5014a029405280a5030363253330403037304137540022900009bad30453042375400264a666080606e60826ea8004530103d87a8000132330010013756608c60866ea8008894ccc114004530103d87a800013232325333045300e375c608c00626062660926ea00052f5c026600a00a0046eb4c118008c124008c11c004cc0a8dd5981318209baa01502c3371e6eb8c10cc110c110c110c110c110c100dd500900b18031bae30423043304330433043303f375402266e3cdd71820982118211821181f1baa0100293371206a6466e00c004dd69811981f1baa0103001375a6048607c6ea804094ccc0f0cdc4000a4000266e05200000110013370e6eb4c080c0f0dd500701b19b8f011330034890550494c4f540030043005375a603e60766ea8068c004cc009220104534849500030033004375a603c60746ea8064dc78091119b8a0020012373000246e64cc008dd4000a441003001001222533333303c00213232323232323300b0020013371491010128000025333039337100069007099b80483c80400c54ccc0e4cdc4001a410004266e00cdc0241002800690068b299981d800899b8a4881035b5d2900004133714911035b5f2000375c607466600e00266ec1300102415d00375266e292210129000042233760980103422c2000375266601001000466e28dd7181d8009bae303c001375860720046eb4c0dc004c8cdd81ba83037001374e60700026ea80084c94ccc0e40044cdc5245027b7d00002133714911037b5f2000375c607064646600200200644a66607800220062664466ec130103422c20003752666012012607800466e29221023a2000333009009303d002337146eb8c0f0004dd7181e800981f00099801001181f80099bb04c10342207d0037520046eac0084c94ccc0e40044cdc52441025b5d00002133714911035b5f2000375c607066600a00266ec1300102415d0037520044466ec1300103422c2000375266600c00c00466e28dd7181c8009bae303a001375800426600a6eb40080044c8c8cdc524410268270000132333001001337006e3400920013371491101270000322253330393371000490000800899191919980300319b8000548004cdc599b80002533303c33710004900a0a40c02903719b8b33700002a66607866e2000520141481805206e0043370c004901019b8300148080cdc70020011bae00222232330010010042253330390011004133003303b00133002002303c001223233001001003225333034302c00113371491101300000315333034337100029000099b8a489012d003300200233702900000089980299b8400148050cdc599b803370a002900a240c00066002002444a66606266e2400920001001133300300333708004900a19b8b3370066e14009201448180004c058004c0c4c0b8dd50008b180998169baa00116302f001323300100101922533302e00114bd7009919299981699baf3013302f375400405626606200466008008002266008008002606400460600022c6eb8c0a400458c0b0004cc0100408cc01922010550494c4f5400375c60500022c6eb8c09800458c0a4004cc0040348cc00d221045348495000375c604a00244646600200200644a666052002297adef6c60132325333028300500213302c00233004004001133004004001302d002302b001223371e666e312000371a004002004a666040603060426ea80044c8c8c8c94ccc09cc0a800852616375c605000260500046eb4c098004c088dd50008b181218109baa00116300630203754600a60406ea8c08cc080dd50008b19801007119baf300430203754600a60406ea80040744c94ccc078c058c07cdd5000899299980f980198101baa00113232325333025302800c153330223370e90009bad302400115333022301a375a601260486ea800c4c068dd6980518121baa00314a02c2c604c0166012002604860426ea800458c018c080dd5180298101baa3023302037540022c6600401c466ebcc010c080dd5180298101baa00101c370e90021119198008008019129998110008a60103d87a800013232533302130050021300d330250024bd7009980200200098130011812000918100009180f98100009180f180f980f8009299980c1808180c9baa00113232323232323232323232325333027302a002149858dd7181400098140011bae30260013026002375c604800260480046eb4c088004c088008dd6981000098100011bad301e001301a37540022c64a66602e601c60306ea800452f5bded8c026eacc070c064dd500099800991980080080291299980d8008a5eb7bdb1804c8c8c8c94ccc070cdc7a45000021003133020337606ea4008dd3000998030030019bab301d003375c6036004603e004603a00200644646600200200644a666038002298103d87a80001323232533301c3371e00c6eb8c07400c4c020cc080dd3000a5eb804cc014014008dd5980e8011810001180f0009ba548000dd7180c180a9baa006163756602e603060300046eb0c058004c058c058008dd6180a00098081baa301300230123013001300e375400229309b2b19299980598018008a99980718069baa00414985854ccc02cc00800454ccc038c034dd50020a4c2c2c60166ea800cdc3a40046e1d2000375a0026eb40055cd2ab9d5573caae7d5d02ba15744ae91", + "hash": "16dd8305a2af132cf9481d0aece85c006b73a7a7b046275c9639da95" }, { "title": "spacetime.spend", @@ -120,8 +132,8 @@ } } ], - "compiledCode": "58290100003232322223222253330084a229309b2b1919bb03006001300630070013758002ae695d0aba21", - "hash": "dbe87f98127efdba8e6c83f717f68c6b7e14e58569ed8a8f9584a3e0" + "compiledCode": "590112010000323232323232222322322322533300a4a229309b2b19299980499b87480000044c8c8c8c94ccc040c04800852616375a602000260200046eb4c038004c02cdd50010a99980499b87480080044c8c94ccc038c04000852616375a601c00260166ea800854ccc024cdc3a40080022a66601860166ea800852616153330093370e90030008a99980618059baa00214985858c024dd5000a99980319b8748000c01cdd50008991919191919191919191919299980a980b8010a4c2c6eb8c054004c054008dd7180980098098011bae30110013011002375a601e002601e0046eb4c034004c034008dd6980580098041baa0011632337606012002601260140026eb00055cd2ab9d5573caae7d5d0aba201", + "hash": "1e7937119c90ff4b7ca39a916afebf155d47a963a5f7ce39702ca280" } ], "definitions": { @@ -432,13 +444,13 @@ "title": "ShipyardRedeemer", "anyOf": [ { - "title": "Mint", + "title": "MintShip", "dataType": "constructor", "index": 0, "fields": [] }, { - "title": "Burn", + "title": "BurnShip", "dataType": "constructor", "index": 1, "fields": [] diff --git a/onchain/src/validators/pellet.ak b/onchain/src/validators/pellet.ak index f138e4e..bbcc8b7 100644 --- a/onchain/src/validators/pellet.ak +++ b/onchain/src/validators/pellet.ak @@ -28,6 +28,12 @@ validator(admin_token: AssetClass) { expect InlineDatum(out_datum) = own_output.datum expect out_datum: PelletDatum = out_datum + expect Some(_) = + list.find( + inputs, + fn(input) { utils.is_ship_token_in_utxo(input.output, shipyard_policy) }, + ) + let has_enough_fuel = amount <= fuel let own_input_has_admin_token = quantity_of(own_input.output.value, admin_token.policy, admin_token.name) == 1 @@ -35,9 +41,11 @@ validator(admin_token: AssetClass) { let datum_update_ok = out_datum == PelletDatum { ..datum, fuel: fuel - amount } - has_enough_fuel && own_input_has_admin_token && value_not_changed && datum_update_ok && utils.is_ship_token_input( - inputs, - shipyard_policy, - ) + and { + has_enough_fuel, + own_input_has_admin_token, + value_not_changed, + datum_update_ok, + } } } diff --git a/onchain/src/validators/shipyard.ak b/onchain/src/validators/shipyard.ak index aa04982..8689440 100644 --- a/onchain/src/validators/shipyard.ak +++ b/onchain/src/validators/shipyard.ak @@ -1,12 +1,103 @@ -use aiken/transaction.{ScriptContext} +use aiken/bytearray +use aiken/dict +use aiken/list +use aiken/string +use aiken/transaction.{InlineDatum, Mint, ScriptContext, Transaction} use aiken/transaction/credential.{Address} -use asteria/types.{ShipyardRedeemer} +use aiken/transaction/value +use asteria/types.{AsteriaDatum, + BurnShip, MintShip, ShipDatum, ShipyardRedeemer} +use asteria/utils validator( - _asteria_validator_address: Address, - _spacetime_validator_address: Address, + asteria_validator_address: Address, + spacetime_validator_address: Address, + initial_fuel: Int, + min_distance: Int, ) { - fn mint(_redeemer: ShipyardRedeemer, _ctx: ScriptContext) -> Bool { - True + pub fn mint(redeemer: ShipyardRedeemer, ctx: ScriptContext) -> Bool { + let ScriptContext { transaction, purpose } = ctx + let Transaction { inputs, outputs, mint, .. } = transaction + expect Mint(policy_id) = purpose + let minted_tokens = + mint + |> value.from_minted_value + |> value.tokens(policy_id) + |> dict.to_list() + + when redeemer is { + MintShip -> { + expect Some(asteria_input) = + list.find( + inputs, + fn(input) { input.output.address == asteria_validator_address }, + ) + expect InlineDatum(asteria_datum) = asteria_input.output.datum + expect asteria_datum: AsteriaDatum = asteria_datum + expect [(ship_token_name, 1)] = + list.filter( + minted_tokens, + fn(token) { utils.has_prefix("SHIP", token.1st) }, + ) + expect [(pilot_token_name, 1)] = + list.filter( + minted_tokens, + fn(token) { utils.has_prefix("PILOT", token.1st) }, + ) + + expect [ship_state] = + list.filter( + outputs, + fn(output) { output.address == spacetime_validator_address }, + ) + expect InlineDatum(ship_datum) = ship_state.datum + expect ship_datum: ShipDatum = ship_datum + + let must_respect_ship_name = + ship_token_name == bytearray.concat( + "SHIP", + bytearray.from_string(string.from_int(asteria_datum.ship_counter)), + ) + let must_respect_pilot_name = + pilot_token_name == bytearray.concat( + "PILOT", + bytearray.from_string(string.from_int(asteria_datum.ship_counter)), + ) + let must_mint_two_assets = list.length(minted_tokens) == 2 + let must_have_initial_fuel = ship_datum.fuel == initial_fuel + let must_respect_min_distance = + utils.distance(ship_datum.pos_x, ship_datum.pos_y) >= min_distance + let must_have_policy = ship_datum.shipyard_policy == policy_id + let must_have_ship_name = ship_datum.ship_token_name == ship_token_name + let must_have_pilot_name = + ship_datum.pilot_token_name == pilot_token_name + let must_hold_ship_token = + value.quantity_of(ship_state.value, policy_id, ship_token_name) == 1 + + and { + must_mint_two_assets, + must_respect_ship_name, + must_respect_pilot_name, + must_have_initial_fuel, + must_respect_min_distance, + must_have_policy, + must_have_ship_name, + must_have_pilot_name, + must_hold_ship_token, + } + } + + BurnShip -> { + expect Some(ship_input) = + list.find( + inputs, + fn(input) { input.output.address == spacetime_validator_address }, + ) + expect InlineDatum(ship_datum) = ship_input.output.datum + expect ship_datum: ShipDatum = ship_datum + expect [(_, -1)] = minted_tokens + ship_datum.pos_x == 0 && ship_datum.pos_y == 0 + } + } } } diff --git a/onchain/src/validators/tests/pellet_tests.ak b/onchain/src/validators/tests/pellet.ak similarity index 89% rename from onchain/src/validators/tests/pellet_tests.ak rename to onchain/src/validators/tests/pellet.ak index a5865ab..d34b60d 100644 --- a/onchain/src/validators/tests/pellet_tests.ak +++ b/onchain/src/validators/tests/pellet.ak @@ -102,10 +102,10 @@ fn provide(options: ProvideTestOptions) -> Bool { let value = from_lovelace(2_000_000) |> add( - options.shipyard_policy, - options.ship_token_name, - options.ship_token_amount, - ) + options.shipyard_policy, + options.ship_token_name, + options.ship_token_amount, + ) Output { address, value, datum: NoDatum, reference_script: None } } let output_reference = @@ -159,27 +159,27 @@ fn provide(options: ProvideTestOptions) -> Bool { extra_signatories: [], redeemers: dict.new() |> dict.insert( - key: Spend( - OutputReference { - transaction_id: TransactionId { hash: transaction_id_1 }, - output_index: 0, - }, - ), - value: { - let redeemer_data: Data = redeemer - redeemer_data - }, - compare: test_utils.script_purpose_compare, - ), + key: Spend( + OutputReference { + transaction_id: TransactionId { hash: transaction_id_1 }, + output_index: 0, + }, + ), + value: { + let redeemer_data: Data = redeemer + redeemer_data + }, + compare: test_utils.script_purpose_compare, + ), datums: dict.new() |> dict.insert( - transaction_id_1, - { - let datum_data: Data = InlineDatum(datum_in) - datum_data - }, - compare: bytearray.compare, - ), + transaction_id_1, + { + let datum_data: Data = InlineDatum(datum_in) + datum_data + }, + compare: bytearray.compare, + ), id: TransactionId { hash: transaction_id_1 }, } let spend_ctx = diff --git a/onchain/src/validators/tests/shipyard.ak b/onchain/src/validators/tests/shipyard.ak new file mode 100644 index 0000000..b1f29f4 --- /dev/null +++ b/onchain/src/validators/tests/shipyard.ak @@ -0,0 +1,413 @@ +use aiken/dict +use aiken/interval.{Finite, Interval, IntervalBound} +use aiken/transaction.{ + InlineDatum, Input, Mint, Output, OutputReference, ScriptContext, Transaction, + TransactionId, +} +use aiken/transaction/credential.{Address, ScriptCredential} +use aiken/transaction/value.{AssetName, PolicyId, Value, add, from_lovelace} +use asteria/test_utils +use asteria/types.{AsteriaDatum, BurnShip, MintShip, ShipDatum} +use shipyard + +// ============================================================================================== +// Mint Tests +// ============================================================================================== + +type MintTestOptions { + initial_fuel: Int, + initial_x: Int, + initial_y: Int, + ship_counter: Int, + shipyard_policy: PolicyId, + ship_token_name: AssetName, + pilot_token_name: AssetName, + no_asteria_input: Bool, + no_ship_output: Bool, + ship_datum: ShipDatum, + ship_value: Value, +} + +type MintTestArgs { + initial_fuel: Int, + initial_x: Int, + initial_y: Int, + ship_counter: Int, + ship_token_name: AssetName, + pilot_token_name: AssetName, + no_asteria_input: Bool, + no_ship_output: Bool, +} + +fn default_args() { + MintTestArgs { + initial_fuel: 40, + initial_x: 10, + initial_y: 10, + ship_counter: 7, + ship_token_name: "SHIP7", + pilot_token_name: "PILOT7", + no_asteria_input: False, + no_ship_output: False, + } +} + +fn set_mint_test_options(args: MintTestArgs) -> MintTestOptions { + let shipyard_policy = + #"0298aa99f95e2fe0a0132a6bb794261fb7e7b0d988215da2f2de2005" + let ship_datum = + ShipDatum { + fuel: args.initial_fuel, + pos_x: args.initial_x, + pos_y: args.initial_y, + shipyard_policy, + ship_token_name: args.ship_token_name, + pilot_token_name: args.pilot_token_name, + } + let ship_value = + from_lovelace(2_000_000) + |> add(shipyard_policy, args.ship_token_name, 1) + MintTestOptions { + initial_fuel: args.initial_fuel, + initial_x: args.initial_x, + initial_y: args.initial_y, + ship_counter: args.ship_counter, + ship_token_name: args.ship_token_name, + pilot_token_name: args.pilot_token_name, + no_asteria_input: args.no_asteria_input, + no_ship_output: args.no_ship_output, + shipyard_policy, + ship_datum, + ship_value, + } +} + +fn ship_mint(options: MintTestOptions) -> Bool { + let shipyard_policy = options.shipyard_policy + let ship_credential = + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306514" + let ship_addr = + Address { + payment_credential: ScriptCredential(ship_credential), + stake_credential: None, + } + let asteria_credential = + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513" + let asteria_addr = + Address { + payment_credential: ScriptCredential(asteria_credential), + stake_credential: None, + } + let admin_policy = #"0298aa99f95e2fe0a0132a6bb794261fb7e7b0d988215da2f2de2004" + let admin_token_name = "admin" + let transaction_id = + #"6dcd4ce23d88e2ee95838f4b759b3456c63d219231a64a3ce6dd2bf72f5c5b6a" + let redeemer = MintShip + let asteria_datum = + AsteriaDatum { ship_counter: options.ship_counter, shipyard_policy } + let asteria_in = { + let output = { + let value = + from_lovelace(2_000_000) + |> add(admin_policy, admin_token_name, 1) + Output { + address: asteria_addr, + value, + datum: InlineDatum(asteria_datum), + reference_script: None, + } + } + Input { + output_reference: OutputReference { + transaction_id: TransactionId { hash: transaction_id }, + output_index: 0, + }, + output, + } + } + let ship_out = + Output { + address: ship_addr, + value: options.ship_value, + datum: InlineDatum(options.ship_datum), + reference_script: None, + } + let tx = + Transaction { + inputs: if options.no_asteria_input { + [] + } else { + [asteria_in] + }, + reference_inputs: [], + outputs: if options.no_ship_output { + [] + } else { + [ship_out] + }, + fee: value.from_lovelace(5_000), + mint: value.to_minted_value( + value.from_asset(shipyard_policy, options.ship_token_name, 1) + |> value.add(shipyard_policy, options.pilot_token_name, 1), + ), + certificates: [], + withdrawals: dict.new(), + validity_range: Interval { + lower_bound: IntervalBound { bound_type: Finite(1), is_inclusive: True }, + upper_bound: IntervalBound { + bound_type: Finite(10), + is_inclusive: True, + }, + }, + extra_signatories: [], + redeemers: dict.new() + |> dict.insert( + key: Mint(shipyard_policy), + value: { + let redeemer_data: Data = redeemer + redeemer_data + }, + compare: test_utils.script_purpose_compare, + ), + datums: dict.new(), + id: TransactionId { hash: transaction_id }, + } + let mint_ctx = + ScriptContext { transaction: tx, purpose: Mint(shipyard_policy) } + let result = + shipyard.mint( + asteria_addr, + ship_addr, + initial_fuel: 40, + min_distance: 15, + redeemer: redeemer, + ctx: mint_ctx, + ) + result +} + +test mint_ok() { + ship_mint(set_mint_test_options(default_args())) +} + +test no_asteria_input() fail { + let args = MintTestArgs { ..default_args(), no_asteria_input: True } + ship_mint(set_mint_test_options(args)) +} + +test wrong_ship_token_prefix() fail { + let args = MintTestArgs { ..default_args(), ship_token_name: "SHI7" } + ship_mint(set_mint_test_options(args)) +} + +test wrong_ship_token_count() fail { + let args = MintTestArgs { ..default_args(), ship_token_name: "SHIP8" } + ship_mint(set_mint_test_options(args)) +} + +test wrong_pilot_token_prefix() fail { + let args = MintTestArgs { ..default_args(), pilot_token_name: "PILO7" } + ship_mint(set_mint_test_options(args)) +} + +test wrong_pilot_token_count() fail { + let args = MintTestArgs { ..default_args(), pilot_token_name: "PILOT8" } + ship_mint(set_mint_test_options(args)) +} + +test no_ship_output() fail { + let args = MintTestArgs { ..default_args(), no_ship_output: True } + ship_mint(set_mint_test_options(args)) +} + +test wrong_initial_fuel() fail { + let args = MintTestArgs { ..default_args(), initial_fuel: 30 } + ship_mint(set_mint_test_options(args)) +} + +test initial_distance_below_min() fail { + let args = MintTestArgs { ..default_args(), initial_x: 3, initial_y: 3 } + ship_mint(set_mint_test_options(args)) +} + +test wrong_policy_in_datum() fail { + let default_options = set_mint_test_options(default_args()) + let wrong_datum = + ShipDatum { ..default_options.ship_datum, shipyard_policy: #"0000" } + let options = MintTestOptions { ..default_options, ship_datum: wrong_datum } + ship_mint(options) +} + +test wrong_ship_name_in_datum() fail { + let default_options = set_mint_test_options(default_args()) + let wrong_datum = + ShipDatum { ..default_options.ship_datum, ship_token_name: "FOO" } + let options = MintTestOptions { ..default_options, ship_datum: wrong_datum } + ship_mint(options) +} + +test wrong_pilot_name_in_datum() fail { + let default_options = set_mint_test_options(default_args()) + let wrong_datum = + ShipDatum { ..default_options.ship_datum, pilot_token_name: "FOO" } + let options = MintTestOptions { ..default_options, ship_datum: wrong_datum } + ship_mint(options) +} + +test ship_token_not_paid() fail { + let wrong_value = value.from_lovelace(2_000_000) + let options = + MintTestOptions { + ..set_mint_test_options(default_args()), + ship_value: wrong_value, + } + ship_mint(options) +} + +// ============================================================================================== +// Burn Tests +// ============================================================================================== + +type BurnTestOptions { + pos_x: Int, + pos_y: Int, + ship_addr: Address, + shipyard_policy: PolicyId, + no_ship_input: Bool, + burnt_value: Value, +} + +fn set_burn_test_options() -> BurnTestOptions { + let shipyard_policy = + #"0298aa99f95e2fe0a0132a6bb794261fb7e7b0d988215da2f2de2005" + let ship_credential = + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306514" + let ship_addr = + Address { + payment_credential: ScriptCredential(ship_credential), + stake_credential: None, + } + let burnt_value = value.from_asset(shipyard_policy, "SHIP7", -1) + BurnTestOptions { + pos_x: 0, + pos_y: 0, + ship_addr, + shipyard_policy, + no_ship_input: False, + burnt_value, + } +} + +fn ship_burn(options: BurnTestOptions) -> Bool { + let ship_token_name = "SHIP7" + let pilot_token_name = "PILOT7" + let shipyard_policy = options.shipyard_policy + let transaction_id = + #"6dcd4ce23d88e2ee95838f4b759b3456c63d219231a64a3ce6dd2bf72f5c5b6a" + let asteria_credential = + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513" + let asteria_addr = + Address { + payment_credential: ScriptCredential(asteria_credential), + stake_credential: None, + } + let redeemer = BurnShip + let ship_datum = + ShipDatum { + fuel: 40, + pos_x: options.pos_x, + pos_y: options.pos_y, + shipyard_policy, + ship_token_name, + pilot_token_name, + } + let ship_in = { + let output = { + let value = + from_lovelace(2_000_000) + |> add(shipyard_policy, ship_token_name, 1) + Output { + address: options.ship_addr, + value, + datum: InlineDatum(ship_datum), + reference_script: None, + } + } + Input { + output_reference: OutputReference { + transaction_id: TransactionId { hash: transaction_id }, + output_index: 0, + }, + output, + } + } + let tx = + Transaction { + inputs: if options.no_ship_input { + [] + } else { + [ship_in] + }, + reference_inputs: [], + outputs: [], + fee: value.from_lovelace(5_000), + mint: value.to_minted_value(options.burnt_value), + certificates: [], + withdrawals: dict.new(), + validity_range: Interval { + lower_bound: IntervalBound { bound_type: Finite(1), is_inclusive: True }, + upper_bound: IntervalBound { + bound_type: Finite(10), + is_inclusive: True, + }, + }, + extra_signatories: [], + redeemers: dict.new() + |> dict.insert( + key: Mint(shipyard_policy), + value: { + let redeemer_data: Data = redeemer + redeemer_data + }, + compare: test_utils.script_purpose_compare, + ), + datums: dict.new(), + id: TransactionId { hash: transaction_id }, + } + let mint_ctx = + ScriptContext { transaction: tx, purpose: Mint(shipyard_policy) } + let result = + shipyard.mint( + asteria_addr, + spacetime_validator_address: options.ship_addr, + initial_fuel: 40, + min_distance: 15, + redeemer: redeemer, + ctx: mint_ctx, + ) + result +} + +test burn_ok() { + ship_burn(set_burn_test_options()) +} + +test no_ship_input() fail { + let options = + BurnTestOptions { ..set_burn_test_options(), no_ship_input: True } + ship_burn(options) +} + +test ship_not_at_center() fail { + let options = BurnTestOptions { ..set_burn_test_options(), pos_x: 1 } + ship_burn(options) +} + +test burn_more_than_one() fail { + let def_options = set_burn_test_options() + let burnt_value = + value.from_asset(def_options.shipyard_policy, "SHIP7", -1) + |> value.add(def_options.shipyard_policy, "PILOT7", -1) + let options = BurnTestOptions { ..def_options, burnt_value: burnt_value } + ship_burn(options) +}