Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ As the result of the discovery process, after surveying many developers, it was

In this repository we include the [on-chain code](./game/onchain/), which comes with a [design document](./game/onchain/docs/design/design.md) that thoroughly explains the transactions involved in the game. In the game [src](./game/src/) you can find the implementation of the commands necessary to play the game and a [README](./game/README.md) that goes over the app design, in the context of the game's integration into the node, and [instructions](./game/README.md#game-usage) on how to run the commands.

![Asteria Game Board](./game/game-board.png "Game Board")

## Setting up a Partner Chain

The project includes the `partner-chains-cli` from IOG's [Partner Chain SDK](https://github.com/input-output-hk/partner-chains). This `CLI` allows us to set up governance UTxOs on the Cardano side for the partner chain. An extensive explanation on the integration and usage of the CLI can be found at the previously mentioned [partner_chain_integration](./docs/dev_logs/partner_chain_integration.md) document.
Expand Down
1 change: 1 addition & 0 deletions game/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition.workspace = true
[dependencies]
anyhow = { workspace = true }
clap = { features = ["derive"], workspace = true }
colored = { workspace = true }
griffin-core = { workspace = true }
gpc-wallet = { workspace = true }
hex = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions game/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,6 @@ This command can be triggered when the ship reaches Asteria, i.e., its coordinat
- *mine-coin-amount*: amount of coin to be mined from Asteria. This must be less or equal to the allowed maximum percentage of the total prize defined by MAX_ASTERIA_MINING.
- *params-path*: path to the JSON file containing all the game scripts parameters and the applied scripts directory.

### Queries

There are also three query commands that allow the user to inspect the game state UTxOs, with their datums in a human-readable format: `show-asteria`, `show-pellets`, and `show-ships`. Each of them takes no arguments, except for the `params-path` argument that is common to all game commands.
Binary file added game/game-board.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
216 changes: 5 additions & 211 deletions game/src/game.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use crate::{CreateShipArgs, DeployScriptsArgs, GatherFuelArgs, MineAsteriaArgs, MoveShipArgs};
use crate::{
types::*, CreateShipArgs, DeployScriptsArgs, GatherFuelArgs, MineAsteriaArgs, MoveShipArgs,
};
use anyhow::anyhow;
use gpc_wallet::{
cli::{ShowOutputsAtArgs, ShowOutputsWithAssetArgs},
keystore, sync,
};
use griffin_core::{
checks_interface::{babbage_minted_tx_from_cbor, babbage_tx_to_cbor},
h224::H224,
pallas_codec::{
minicbor,
utils::{Int, MaybeIndefArray::Indef},
},
pallas_crypto::hash::Hash as PallasHash,
pallas_primitives::{
babbage::{
BigInt, BoundedBytes, Constr, MintedTx, PlutusData as PallasPlutusData,
Expand All @@ -29,7 +29,6 @@ use griffin_core::{
use jsonrpsee::{core::client::ClientT, http_client::HttpClient, rpc_params};
use parity_scale_codec::Encode;
use sc_keystore::LocalKeystore;
use serde::{Deserialize, Serialize};
use sled::Db;
use sp_core::ed25519::Public;
use sp_runtime::traits::{BlakeTwo256, Hash};
Expand Down Expand Up @@ -836,7 +835,7 @@ pub async fn mine_asteria(
transaction.transaction_body.inputs = inputs;

// BURNS
let ship_fuel = quanity_of(&ship_value, &pellet_policy, &fuel_name);
let ship_fuel = (&ship_value).quantity_of(&pellet_policy, &fuel_name);
let ship_fuel_i64: i64 = ship_fuel
.try_into()
.expect("Fuel amount too large to fit in i64");
Expand Down Expand Up @@ -901,7 +900,7 @@ pub async fn mine_asteria(
address: pilot_utxo.address.clone(),
value: pilot_utxo.value.clone()
+ Value::Coin(args.mine_coin_amount)
+ Value::Coin(coin_of(&ship_value)),
+ Value::Coin((&ship_value).coin_of()),
datum_option: pilot_utxo.datum_option.clone(),
},
Output {
Expand Down Expand Up @@ -1131,208 +1130,3 @@ pub async fn deploy_scripts(args: DeployScriptsArgs) -> anyhow::Result<()> {
println!("All scripts written successfully!");
Ok(())
}

fn quanity_of(value: &Value, policy: &PolicyId, name: &AssetName) -> u64 {
if let Value::Multiasset(_, ma) = value {
if let Some(assets) = ma.0.get(policy) {
if let Some(quantity) = assets.0.get(name) {
return *quantity as u64;
}
}
}
0
}

fn coin_of(value: &Value) -> u64 {
match value {
Value::Coin(c) => *c,
Value::Multiasset(c, _) => *c,
}
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct ScriptsParams {
admin_policy: String,
admin_name: String,
fuel_per_step: u64,
initial_fuel: u64,
max_speed: Speed,
max_ship_fuel: u64,
max_asteria_mining: u64,
min_asteria_distance: u64,
ship_mint_lovelace_fee: u64,
scripts_directory: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Speed {
distance: u64,
time: u64,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AsteriaDatum {
Ok {
ship_counter: u16,
shipyard_policy: PolicyId,
},
MalformedAsteriaDatum,
}

impl From<AsteriaDatum> for Datum {
fn from(order_datum: AsteriaDatum) -> Self {
Datum(PlutusData::from(PallasPlutusData::from(order_datum)).0)
}
}

impl From<Datum> for AsteriaDatum {
fn from(datum: Datum) -> Self {
<_>::from(PallasPlutusData::from(PlutusData(datum.0)))
}
}

impl From<AsteriaDatum> for PallasPlutusData {
fn from(order_datum: AsteriaDatum) -> Self {
match order_datum {
AsteriaDatum::Ok {
ship_counter,
shipyard_policy,
} => PallasPlutusData::from(PallasPlutusData::Constr(Constr {
tag: 121,
any_constructor: None,
fields: Indef(
[
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(
ship_counter,
)))),
PallasPlutusData::BoundedBytes(BoundedBytes(shipyard_policy.0.to_vec())),
]
.to_vec(),
),
})),
AsteriaDatum::MalformedAsteriaDatum => {
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(-1))))
}
}
}
}

impl From<PallasPlutusData> for AsteriaDatum {
fn from(data: PallasPlutusData) -> Self {
if let PallasPlutusData::Constr(Constr {
tag: 121,
any_constructor: None,
fields: Indef(asteria_datum),
}) = data
{
if let [PallasPlutusData::BigInt(BigInt::Int(Int(ship_counter))), PallasPlutusData::BoundedBytes(BoundedBytes(shipyard_policy_vec))] =
&asteria_datum[..]
{
AsteriaDatum::Ok {
ship_counter: TryFrom::<minicbor::data::Int>::try_from(*ship_counter).unwrap(),
shipyard_policy: H224::from(PallasHash::from(shipyard_policy_vec.as_slice())),
}
} else {
AsteriaDatum::MalformedAsteriaDatum
}
} else {
AsteriaDatum::MalformedAsteriaDatum
}
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ShipDatum {
Ok {
pos_x: i16,
pos_y: i16,
ship_token_name: AssetName,
pilot_token_name: AssetName,
last_move_latest_time: u64,
},
MalformedShipDatum,
}

impl From<ShipDatum> for Datum {
fn from(ship_datum: ShipDatum) -> Self {
Datum(PlutusData::from(PallasPlutusData::from(ship_datum)).0)
}
}

impl From<Datum> for ShipDatum {
fn from(datum: Datum) -> Self {
<_>::from(PallasPlutusData::from(PlutusData(datum.0)))
}
}

impl From<ShipDatum> for PallasPlutusData {
fn from(ship_datum: ShipDatum) -> Self {
match ship_datum {
ShipDatum::Ok {
pos_x,
pos_y,
ship_token_name,
pilot_token_name,
last_move_latest_time,
} => PallasPlutusData::from(PallasPlutusData::Constr(Constr {
tag: 121,
any_constructor: None,
fields: Indef(
[
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(
pos_x,
)))),
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(
pos_y,
)))),
PallasPlutusData::BoundedBytes(BoundedBytes(
ship_token_name.0.clone().into(),
)),
PallasPlutusData::BoundedBytes(BoundedBytes(
pilot_token_name.0.clone().into(),
)),
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(
last_move_latest_time,
)))),
]
.to_vec(),
),
})),
ShipDatum::MalformedShipDatum => {
PallasPlutusData::BigInt(BigInt::Int(Int(minicbor::data::Int::from(-1))))
}
}
}
}

impl From<PallasPlutusData> for ShipDatum {
fn from(data: PallasPlutusData) -> Self {
if let PallasPlutusData::Constr(Constr {
tag: 121,
any_constructor: None,
fields: Indef(ship_datum),
}) = data
{
if let [PallasPlutusData::BigInt(BigInt::Int(Int(pos_x))), PallasPlutusData::BigInt(BigInt::Int(Int(pos_y))), PallasPlutusData::BoundedBytes(BoundedBytes(ship_name_vec)), PallasPlutusData::BoundedBytes(BoundedBytes(pilot_name_vec)), PallasPlutusData::BigInt(BigInt::Int(Int(last_move_latest_time)))] =
&ship_datum[..]
{
ShipDatum::Ok {
pos_x: TryFrom::<minicbor::data::Int>::try_from(*pos_x).unwrap(),
pos_y: TryFrom::<minicbor::data::Int>::try_from(*pos_y).unwrap(),
ship_token_name: AssetName(String::from_utf8(ship_name_vec.to_vec()).unwrap()),
pilot_token_name: AssetName(
String::from_utf8(pilot_name_vec.to_vec()).unwrap(),
),
last_move_latest_time: TryFrom::<minicbor::data::Int>::try_from(
*last_move_latest_time,
)
.unwrap(),
}
} else {
ShipDatum::MalformedShipDatum
}
} else {
ShipDatum::MalformedShipDatum
}
}
}
24 changes: 24 additions & 0 deletions game/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod game;
mod queries;
mod tests;
mod types;

use clap::{Args, Subcommand};
use gpc_wallet::{context::Context, keystore, utils};
Expand All @@ -24,6 +26,12 @@ pub enum Command {
MineAsteria(MineAsteriaArgs),
/// Apply parameters and write game scripts
DeployScripts(DeployScriptsArgs),
/// Show Asteria UTxO
ShowAsteria(ShowAsteriaArgs),
/// Show pellet UTxOs
ShowPellets(ShowPelletsArgs),
/// Show ship UTxOs
ShowShips(ShowShipsArgs),
}

impl GameCommand {
Expand Down Expand Up @@ -66,6 +74,18 @@ impl GameCommand {
let _ = game::deploy_scripts(args).await.unwrap();
Ok(())
}
Command::ShowAsteria(args) => {
let _ = queries::show_asteria(&db, args).await.unwrap();
Ok(())
}
Command::ShowPellets(args) => {
let _ = queries::show_pellets(&db, args).await.unwrap();
Ok(())
}
Command::ShowShips(args) => {
let _ = queries::show_ships(&db, args).await.unwrap();
Ok(())
}
},
None => {
log::info!(" Asteria game");
Expand Down Expand Up @@ -276,3 +296,7 @@ pub struct DeployScriptsArgs {
)]
pub params_path: String,
}

pub type ShowAsteriaArgs = DeployScriptsArgs;
pub type ShowPelletsArgs = DeployScriptsArgs;
pub type ShowShipsArgs = DeployScriptsArgs;
Loading