From 1f76f1acf705f1fce343e9cd7143731cc1befe74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Tue, 9 Aug 2022 08:23:30 +0200 Subject: [PATCH] Implementation of RPS game to demonstrate STF / runtime use-case Based on the Rock-Paper-Scissors game --- .github/workflows/build_and_test.yml | 3 + Cargo.lock | 17 ++++ app-libs/sgx-runtime/Cargo.toml | 4 + app-libs/sgx-runtime/src/lib.rs | 7 ++ app-libs/stf/Cargo.toml | 3 + app-libs/stf/src/helpers.rs | 26 ++++- app-libs/stf/src/lib.rs | 11 +++ app-libs/stf/src/stf_sgx.rs | 42 ++++++++- cli/Cargo.toml | 3 + cli/demo_rps.sh | 105 +++++++++++++++++++++ cli/src/main.rs | 1 + cli/src/rps_commands.rs | 136 +++++++++++++++++++++++++++ cli/src/trusted_commands.rs | 47 ++++++++- docker/README.md | 11 +++ docker/demo-rps.yml | 21 +++++ enclave-runtime/Cargo.lock | 16 ++++ 16 files changed, 449 insertions(+), 4 deletions(-) create mode 100755 cli/demo_rps.sh create mode 100644 cli/src/rps_commands.rs create mode 100644 docker/demo-rps.yml diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 1ced6cd5ae..25a4a0e602 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -142,6 +142,9 @@ jobs: - test: Benchmark mode: sidechain demo_name: sidechain-benchmark + - test: RPS + mode: sidechain + demo_name: demo-rps steps: - uses: actions/checkout@v3 diff --git a/Cargo.lock b/Cargo.lock index 3c3306b8b4..181ffc99f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2347,6 +2347,7 @@ dependencies = [ "itp-utils", "log 0.4.17", "pallet-balances", + "pallet-jton_rps", "parity-scale-codec", "primitive-types", "rand 0.8.5", @@ -2550,6 +2551,7 @@ dependencies = [ "pallet-balances", "pallet-evm", "pallet-grandpa", + "pallet-jton_rps", "pallet-parentchain", "pallet-randomness-collective-flip", "pallet-sudo", @@ -2589,6 +2591,7 @@ dependencies = [ "its-state", "log 0.4.17", "pallet-balances", + "pallet-jton_rps", "parity-scale-codec", "sc-keystore", "sgx_tstd", @@ -4777,6 +4780,20 @@ dependencies = [ "sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)", ] +[[package]] +name = "pallet-jton_rps" +version = "0.1.0" +source = "git+https://github.com/integritee-network/pallet-jton-rps.git?branch=integritee-demo#6a8e6dcc579c32d97d4383bc2b5222eaf1f24b88" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io 6.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)", + "sp-runtime", + "sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)", +] + [[package]] name = "pallet-multisig" version = "4.0.0-dev" diff --git a/app-libs/sgx-runtime/Cargo.toml b/app-libs/sgx-runtime/Cargo.toml index d9b943547e..6fdbf00869 100644 --- a/app-libs/sgx-runtime/Cargo.toml +++ b/app-libs/sgx-runtime/Cargo.toml @@ -41,6 +41,9 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr sp-transaction-pool = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } sp-version = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } +# use case specific +pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" } + # Integritee dependencies pallet-parentchain = { default-features = false, git = "https://github.com/integritee-network/pallets.git", branch = "master" } pallet-evm = { default-features = false, optional = true, git = "https://github.com/integritee-network/frontier.git", branch = "polkadot-v0.9.26" } @@ -76,6 +79,7 @@ std = [ "pallet-balances/std", "pallet-grandpa/std", "pallet-randomness-collective-flip/std", + "pallet-rps/std", "pallet-sudo/std", "pallet-timestamp/std", "pallet-transaction-payment/std", diff --git a/app-libs/sgx-runtime/src/lib.rs b/app-libs/sgx-runtime/src/lib.rs index e9d94725e0..b9acfcf761 100644 --- a/app-libs/sgx-runtime/src/lib.rs +++ b/app-libs/sgx-runtime/src/lib.rs @@ -63,7 +63,9 @@ pub use frame_support::{ }; pub use pallet_balances::Call as BalancesCall; pub use pallet_parentchain::Call as ParentchainCall; +pub use pallet_rps::Call as RpsCall; pub use pallet_timestamp::Call as TimestampCall; + #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; @@ -290,6 +292,10 @@ impl pallet_parentchain::Config for Runtime { type WeightInfo = (); } +impl pallet_rps::Config for Runtime { + type Event = Event; +} + // The plain sgx-runtime without the `evm-pallet` #[cfg(not(feature = "evm"))] construct_runtime!( @@ -304,6 +310,7 @@ construct_runtime!( TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, Parentchain: pallet_parentchain::{Pallet, Call, Storage}, + Rps: pallet_rps::{Pallet, Call, Storage, Event}, } ); diff --git a/app-libs/stf/Cargo.toml b/app-libs/stf/Cargo.toml index d4d16d463a..d8d7a5f503 100644 --- a/app-libs/stf/Cargo.toml +++ b/app-libs/stf/Cargo.toml @@ -21,6 +21,9 @@ itp-storage = { default-features = false, path = "../../core-primitives/storage" its-state = { default-features = false, optional = true, path = "../../sidechain/state" } sp-io = { optional = true, default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } +# use case specific +pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" } + # Substrate dependencies sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } balances = { package = "pallet-balances", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } diff --git a/app-libs/stf/src/helpers.rs b/app-libs/stf/src/helpers.rs index 8d5e82d76a..7d938ffd42 100644 --- a/app-libs/stf/src/helpers.rs +++ b/app-libs/stf/src/helpers.rs @@ -15,7 +15,8 @@ */ use crate::{ - stf_sgx_primitives::types::*, AccountId, Index, StfError, StfResult, ENCLAVE_ACCOUNT_KEY, H256, + stf_sgx_primitives::types::*, AccountId, Game, Hash, Index, StfError, StfResult, + ENCLAVE_ACCOUNT_KEY, H256, }; use codec::{Decode, Encode}; use itp_storage::{storage_double_map_key, storage_map_key, storage_value_key, StorageHasher}; @@ -193,3 +194,26 @@ pub fn ensure_root(account: AccountId) -> StfResult<()> { Err(StfError::MissingPrivileges(account)) } } + +pub fn get_block_number() -> BlockNumber { + get_storage_value("System", "Number").unwrap() +} + +pub fn get_game_for(who: AccountId) -> Option { + if let Some(game_id) = + get_storage_map::("Rps", "PlayerGame", &who, &StorageHasher::Identity) + { + if let Some(game) = + get_storage_map::("Rps", "Games", &game_id, &StorageHasher::Identity) + { + info!("Game state for {:x?} is: {:?}", game.players, game.states); + Some(game) + } else { + debug!("could not read game"); + None + } + } else { + debug!("could not read game id"); + None + } +} diff --git a/app-libs/stf/src/lib.rs b/app-libs/stf/src/lib.rs index 7868939aea..eb5ccd649e 100644 --- a/app-libs/stf/src/lib.rs +++ b/app-libs/stf/src/lib.rs @@ -33,6 +33,7 @@ pub use my_node_runtime::{Balance, Index}; use codec::{Compact, Decode, Encode}; use derive_more::Display; +use pallet_rps::Game as GameT; use sp_core::{crypto::AccountId32, ed25519, sr25519, Pair, H256}; use sp_runtime::{traits::Verify, MultiSignature}; use std::string::String; @@ -47,6 +48,8 @@ pub type ShardIdentifier = H256; pub type StfResult = Result; +pub type Game = GameT; + #[derive(Debug, Display, PartialEq, Eq)] pub enum StfError { #[display(fmt = "Insufficient privileges {:?}, are you sure you are root?", _0)] @@ -181,6 +184,9 @@ pub enum TrustedCall { balance_transfer(AccountId, AccountId, Balance), balance_unshield(AccountId, AccountId, Balance, ShardIdentifier), // (AccountIncognito, BeneficiaryPublicAccount, Amount, Shard) balance_shield(AccountId, AccountId, Balance), // (Root, AccountIncognito, Amount) + rps_new_game(AccountId, AccountId), + rps_choose(AccountId, pallet_rps::WeaponType), + rps_reveal(AccountId, pallet_rps::WeaponType), } impl TrustedCall { @@ -190,6 +196,9 @@ impl TrustedCall { TrustedCall::balance_transfer(account, _, _) => account, TrustedCall::balance_unshield(account, _, _, _) => account, TrustedCall::balance_shield(account, _, _) => account, + TrustedCall::rps_new_game(account, _) => account, + TrustedCall::rps_choose(account, _) => account, + TrustedCall::rps_reveal(account, _) => account, } } @@ -215,6 +224,7 @@ pub enum TrustedGetter { free_balance(AccountId), reserved_balance(AccountId), nonce(AccountId), + game(AccountId), } impl TrustedGetter { @@ -223,6 +233,7 @@ impl TrustedGetter { TrustedGetter::free_balance(account) => account, TrustedGetter::reserved_balance(account) => account, TrustedGetter::nonce(account) => account, + TrustedGetter::game(account) => account, } } diff --git a/app-libs/stf/src/stf_sgx.rs b/app-libs/stf/src/stf_sgx.rs index 44b396a1d1..8405e47982 100644 --- a/app-libs/stf/src/stf_sgx.rs +++ b/app-libs/stf/src/stf_sgx.rs @@ -21,7 +21,7 @@ use crate::test_genesis::test_genesis_setup; use crate::{ helpers::{ account_data, account_nonce, enclave_signer_account, ensure_enclave_signer_account, - ensure_root, get_account_info, increment_nonce, root, validate_nonce, + ensure_root, get_account_info, get_game_for, increment_nonce, root, validate_nonce, }, AccountData, AccountId, Getter, Index, ParentchainHeader, PublicGetter, ShardIdentifier, State, StateTypeDiff, Stf, StfError, StfResult, TrustedCall, TrustedCallSigned, TrustedGetter, @@ -115,6 +115,12 @@ impl Stf { } else { None }, + TrustedGetter::game(who) => + if let Some(game) = get_game_for(who) { + Some(game.encode()) + } else { + None + }, }, Getter::public(g) => match g { PublicGetter::some_value => Some(42u32.encode()), @@ -200,6 +206,37 @@ impl Stf { Self::shield_funds(who, value)?; Ok(()) }, + TrustedCall::rps_new_game(sender, opponent) => { + let origin = ita_sgx_runtime::Origin::signed(sender.clone()); + info!("rps new_game"); + ita_sgx_runtime::RpsCall::::new_game { opponent } + .dispatch_bypass_filter(origin) + .map_err(|_| StfError::Dispatch("rps_new_game".to_string()))?; + Ok(()) + }, + TrustedCall::rps_choose(sender, weapon) => { + let origin = ita_sgx_runtime::Origin::signed(sender.clone()); + info!("rps choose: {:?}", weapon); + ita_sgx_runtime::RpsCall::::choose { + choice: weapon.clone(), + salt: [0u8; 32], + } + .dispatch_bypass_filter(origin.clone()) + .map_err(|e| { + error!("dispatch error {:?}", e); + StfError::Dispatch("rps_choose".to_string()) + })?; + Ok(()) + }, + TrustedCall::rps_reveal(sender, weapon) => { + let origin = ita_sgx_runtime::Origin::signed(sender.clone()); + info!("rps reveal"); + ita_sgx_runtime::RpsCall::::reveal { choice: weapon, salt: [0u8; 32] } + .dispatch_bypass_filter(origin) + .map_err(|_| StfError::Dispatch("rps_reveal".to_string()))?; + get_game_for(sender); + Ok(()) + }, }?; increment_nonce(&sender); Ok(()) @@ -304,6 +341,9 @@ impl Stf { TrustedCall::balance_transfer(_, _, _) => debug!("No storage updates needed..."), TrustedCall::balance_unshield(_, _, _, _) => debug!("No storage updates needed..."), TrustedCall::balance_shield(_, _, _) => debug!("No storage updates needed..."), + TrustedCall::rps_new_game(_, _) => debug!("No storage updates needed..."), + TrustedCall::rps_choose(_, _) => debug!("No storage updates needed..."), + TrustedCall::rps_reveal(_, _) => debug!("No storage updates needed..."), }; key_hashes } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 337a627a52..d92107dde7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -30,6 +30,9 @@ substrate-api-client = { features = ["ws-client"], git = "https://github.com/scs substrate-client-keystore = { git = "https://github.com/scs/substrate-api-client", branch = "polkadot-v0.9.26" } teerex-primitives = { git = "https://github.com/integritee-network/pallets.git", branch = "master" } +# use case specific +pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" } + # substrate dependencies sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } sc-keystore = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" } diff --git a/cli/demo_rps.sh b/cli/demo_rps.sh new file mode 100755 index 0000000000..d50c934a18 --- /dev/null +++ b/cli/demo_rps.sh @@ -0,0 +1,105 @@ +#!/bin/bash +set -euo pipefail + +# setup: +# run all on localhost: +# integritee-node purge-chain --dev +# integritee-node --tmp --dev -lruntime=debug +# rm light_client_db.bin +# integritee-service init_shard +# integritee-service shielding-key +# integritee-service signing-key +# export RUST_LOG=integritee_service=info,ita_stf=debug +# integritee-service run +# +# then run this script + +# usage: +# demo_rps.sh -p -P -m file + +while getopts ":m:p:P:u:V:C:" opt; do + case $opt in + m) + READMRENCLAVE=$OPTARG + ;; + p) + NPORT=$OPTARG + ;; + P) + WORKER1PORT=$OPTARG + ;; + u) + NODEURL=$OPTARG + ;; + V) + WORKER1URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + esac +done + +# Using default port if none given as arguments. +NPORT=${NPORT:-9944} +NODEURL=${NODEURL:-"ws://127.0.0.1"} + +WORKER1PORT=${WORKER1PORT:-2000} +WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +READMRENCLAVE=${READMRENCLAVE:-"onchain-registry"} + +echo "Using client binary ${CLIENT_BIN}" +echo "Using node uri ${NODEURL}:${NPORT}" +echo "Using trusted-worker uri ${WORKER1URL}:${WORKER1PORT}" +echo "Reading MRENCLAVE from ${READMRENCLAVE}" + +CLIENT="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1URL}" + +if [ "$READMRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # this will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +PLAYER1=$($CLIENT trusted --mrenclave "$MRENCLAVE" new-account) +PLAYER2=$($CLIENT trusted --mrenclave "$MRENCLAVE" new-account) + +echo "Alice (sudo) sets initial balances" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct set-balance "${PLAYER1}" 1000 +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct set-balance "${PLAYER2}" 1000 +echo "" + +echo "Alice starts new game against Bob" +# shellcheck disable=SC2086 +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct new-game "${PLAYER1}" "${PLAYER2}" +echo "" + +echo "Alice chooses her weapon" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct choose "${PLAYER1}" Rock +echo "" + +echo "Bob chooses his weapon" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct choose "${PLAYER2}" Paper +echo "" + +echo "Alice reveals" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct reveal "${PLAYER1}" Rock +echo "" + +echo "Bob reveals" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct reveal "${PLAYER2}" Paper +echo "" + +echo "Query result" +${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct get-game "${PLAYER1}" +echo "" + +exit 0 diff --git a/cli/src/main.rs b/cli/src/main.rs index d9f1ac69cc..4939f4984d 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -30,6 +30,7 @@ extern crate log; mod command_utils; mod commands; mod exchange_oracle; +mod rps_commands; mod trusted_command_utils; mod trusted_commands; mod trusted_operation; diff --git a/cli/src/rps_commands.rs b/cli/src/rps_commands.rs new file mode 100644 index 0000000000..c2b41efc72 --- /dev/null +++ b/cli/src/rps_commands.rs @@ -0,0 +1,136 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_nonce, + trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, + trusted_commands::TrustedArgs, + trusted_operation::perform_trusted_operation, + Cli, +}; +use codec::Decode; +use ita_stf::{AccountId, Hash, Index, KeyPair, TrustedCall, TrustedGetter, TrustedOperation}; +use log::*; +use pallet_rps::WeaponType; +use sp_core::{crypto::Ss58Codec, sr25519 as sr25519_core, Pair}; + +/// Create a new RPS game. +pub(crate) fn new_rps_game( + cli: &Cli, + trusted_args: &TrustedArgs, + player_creator: &str, + player_opponent: &str, +) { + let creator = get_pair_from_str(trusted_args, player_creator); + let opponent = get_accountid_from_str(player_opponent); + let direct: bool = trusted_args.direct; + + info!("creator ss58 is {}", creator.public().to_ss58check()); + info!("opponent ss58 is {}", opponent.to_ss58check()); + + println!("send trusted call rps_new_game from {} with opponent {}", creator.public(), opponent); + + let (mrenclave, shard) = get_identifiers(trusted_args); + let nonce = get_layer_two_nonce!(creator, cli, trusted_args); + + let top: TrustedOperation = TrustedCall::rps_new_game(creator.public().into(), opponent) + .sign(&KeyPair::Sr25519(creator), nonce, &mrenclave, &shard) + .into_trusted_operation(direct); + + let _ = perform_trusted_operation(cli, trusted_args, &top); +} + +/// Choose RPS weapon for a player. +pub(crate) fn rps_choose( + cli: &Cli, + trusted_args: &TrustedArgs, + arg_player: &str, + arg_weapon: &str, +) { + let player = get_pair_from_str(trusted_args, arg_player); + let weapon = string_to_weapon(arg_weapon); + let direct: bool = trusted_args.direct; + + info!("player ss58 is {}", player.public().to_ss58check()); + info!("weapon choice is {:?}", weapon); + + println!("send trusted call rps_choose from {} with weapon {:?}", player.public(), weapon); + let (mrenclave, shard) = get_identifiers(trusted_args); + let nonce = get_layer_two_nonce!(player, cli, trusted_args); + let top: TrustedOperation = TrustedCall::rps_choose(player.public().into(), weapon) + .sign(&KeyPair::Sr25519(player), nonce, &mrenclave, &shard) + .into_trusted_operation(direct); + + let _ = perform_trusted_operation(cli, trusted_args, &top); +} + +/// Reveal a player's weapon. +pub(crate) fn rps_reveal( + cli: &Cli, + trusted_args: &TrustedArgs, + arg_player: &str, + arg_weapon: &str, +) { + let player = get_pair_from_str(trusted_args, arg_player); + let weapon = string_to_weapon(arg_weapon); + let direct: bool = trusted_args.direct; + + info!("player ss58 is {}", player.public().to_ss58check()); + info!("weapon choice is {:?}", weapon); + + println!("send trusted call rps_reveal from {} with weapon {:?}", player.public(), weapon); + + let (mrenclave, shard) = get_identifiers(trusted_args); + let nonce = get_layer_two_nonce!(player, cli, trusted_args); + let top: TrustedOperation = TrustedCall::rps_reveal(player.public().into(), weapon) + .sign(&KeyPair::Sr25519(player), nonce, &mrenclave, &shard) + .into_trusted_operation(direct); + let _ = perform_trusted_operation(cli, trusted_args, &top); +} + +/// Query game state for a specific player. +pub(crate) fn rps_get_game(cli: &Cli, trusted_args: &TrustedArgs, arg_player: &str) { + let player = get_pair_from_str(trusted_args, arg_player); + let top: TrustedOperation = TrustedGetter::game(player.public().into()) + .sign(&KeyPair::Sr25519(player)) + .into(); + + let getter_result = perform_trusted_operation(cli, trusted_args, &top); + + debug!("received result for game"); + if let Some(v) = getter_result { + if let Ok(game) = pallet_rps::Game::::decode(&mut v.as_slice()) { + println!("game state for {:?} ", game.id); + println!("player {}: {:?}", game.players[0].to_ss58check(), game.states[0]); + println!("player {}: {:?}", game.players[1].to_ss58check(), game.states[1]); + } else { + println!("could not decode game. maybe hasn't been set? {:x?}", v); + } + } else { + println!("could not fetch game"); + }; +} + +/// Convert a string to a weapon. Panics if conversion fails. +fn string_to_weapon(weapon_str: &str) -> WeaponType { + match weapon_str { + r"Rock" => WeaponType::Rock, + r"Paper" => WeaponType::Paper, + r"Scissors" => WeaponType::Scissors, + _ => panic!("unknown weapon type"), + } +} diff --git a/cli/src/trusted_commands.rs b/cli/src/trusted_commands.rs index a0a2caf470..16082aaaae 100644 --- a/cli/src/trusted_commands.rs +++ b/cli/src/trusted_commands.rs @@ -17,6 +17,7 @@ use crate::{ command_utils::get_worker_api_direct, + rps_commands::{new_rps_game, rps_choose, rps_get_game, rps_reveal}, trusted_command_utils::{ get_accountid_from_str, get_identifiers, get_keystore_path, get_pair_from_str, }, @@ -45,13 +46,14 @@ use std::{ }; use substrate_client_keystore::{KeystoreExt, LocalKeystore}; +#[macro_export] macro_rules! get_layer_two_nonce { ($signer_pair:ident, $cli: ident, $trusted_args:ident ) => {{ let top: TrustedOperation = TrustedGetter::nonce(sr25519_core::Public::from($signer_pair.public()).into()) .sign(&KeyPair::Sr25519($signer_pair.clone())) .into(); - let res = perform_operation($cli, $trusted_args, &top); + let res = perform_trusted_operation($cli, $trusted_args, &top); let nonce: Index = if let Some(n) = res { if let Ok(nonce) = Index::decode(&mut n.as_slice()) { nonce @@ -82,7 +84,7 @@ pub struct TrustedArgs { /// insert if direct invocation call is desired #[clap(short, long)] - direct: bool, + pub(crate) direct: bool, #[clap(subcommand)] command: TrustedCommands, @@ -135,6 +137,39 @@ pub enum TrustedCommands { amount: Balance, }, + /// New game, create new RPS game + NewGame { + /// Creator's AccountId in ss58check format + creator: String, + + /// Opponent's AccountId in ss58check format + opponent: String, + }, + + /// Choose RPS weapon + Choose { + /// Player's AccountId in ss58check format + player: String, + + /// Weapon choice. One of 'Rock', 'Paper' or 'Scissors' + weapon: String, + }, + + /// RPS reveal + Reveal { + /// Player's AccountId in ss58check format + player: String, + + /// Weapon choice. One of 'Rock', 'Paper' or 'Scissors' + weapon: String, + }, + + /// Get Game - query game state for account in keystore + GetGame { + /// Player's AccountId in ss58check format + player: String, + }, + /// Run Benchmark Benchmark { /// The number of clients (=threads) to be used in the benchmark @@ -190,6 +225,14 @@ pub fn match_trusted_commands(cli: &Cli, trusted_args: &TrustedArgs) { *wait_for_confirmation, funding_account, ), + + // RPS Specific commands + // ------------------------------ + TrustedCommands::NewGame { creator, opponent } => + new_rps_game(cli, trusted_args, creator, opponent), + TrustedCommands::Choose { player, weapon } => rps_choose(cli, trusted_args, player, weapon), + TrustedCommands::Reveal { player, weapon } => rps_reveal(cli, trusted_args, player, weapon), + TrustedCommands::GetGame { player } => rps_get_game(cli, trusted_args, player), } } diff --git a/docker/README.md b/docker/README.md index 49ef5b0194..73233fabff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -23,6 +23,16 @@ Starts all services (node and workers), using the `integritee-worker:dev` images ## Run the demos +### Demo RPS (Rock-Paper-Scissors Game) +Build +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose -f docker-compose.yml -f demo-rps.yml build --build-arg WORKER_MODE_ARG=sidechain +``` +Run +``` +docker-compose -f docker-compose.yml -f demo-rps.yml up --exit-code-from demo-rps +``` + ### Demo indirect invocation (M6) Build ``` @@ -32,6 +42,7 @@ Run ``` docker compose -f docker-compose.yml -f demo-indirect-invocation.yml up demo-indirect-invocation --exit-code-from demo-indirect-invocation ``` + ### Demo direct call (M8) Build diff --git a/docker/demo-rps.yml b/docker/demo-rps.yml new file mode 100644 index 0000000000..56eca99190 --- /dev/null +++ b/docker/demo-rps.yml @@ -0,0 +1,21 @@ +version: "3.4" +services: + demo-rps: + image: integritee-cli:dev + container_name: integritee-rps-demo + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: ['integritee-node', 'integritee-worker-1', 'integritee-worker-2'] + networks: + - integritee-test-network + entrypoint: "dockerize -wait http://integritee-worker-2:4646/is_initialized -timeout 250s + /usr/local/worker-cli/demo_rps.sh + -u ws://integritee-node -p 9912 + -V wss://integritee-worker-1 -P 2011 + -C /usr/local/bin/integritee-cli 2>&1" + restart: "no" +networks: + integritee-test-network: + driver: bridge \ No newline at end of file diff --git a/enclave-runtime/Cargo.lock b/enclave-runtime/Cargo.lock index c503d76011..1a53fa2231 100644 --- a/enclave-runtime/Cargo.lock +++ b/enclave-runtime/Cargo.lock @@ -1300,6 +1300,7 @@ dependencies = [ "pallet-aura", "pallet-balances", "pallet-grandpa", + "pallet-jton_rps", "pallet-parentchain", "pallet-randomness-collective-flip", "pallet-sudo", @@ -1337,6 +1338,7 @@ dependencies = [ "its-state", "log", "pallet-balances", + "pallet-jton_rps", "parity-scale-codec", "sgx_tstd", "sidechain-primitives", @@ -2503,6 +2505,20 @@ dependencies = [ "sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)", ] +[[package]] +name = "pallet-jton_rps" +version = "0.1.0" +source = "git+https://github.com/integritee-network/pallet-jton-rps.git?branch=integritee-demo#6a8e6dcc579c32d97d4383bc2b5222eaf1f24b88" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)", +] + [[package]] name = "pallet-parentchain" version = "0.9.0"