Skip to content

Commit 1f76f1a

Browse files
author
Felix Müller
committed
Implementation of RPS game to demonstrate STF / runtime use-case
Based on the Rock-Paper-Scissors game
1 parent e4bc682 commit 1f76f1a

File tree

16 files changed

+449
-4
lines changed

16 files changed

+449
-4
lines changed

.github/workflows/build_and_test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ jobs:
142142
- test: Benchmark
143143
mode: sidechain
144144
demo_name: sidechain-benchmark
145+
- test: RPS
146+
mode: sidechain
147+
demo_name: demo-rps
145148

146149
steps:
147150
- uses: actions/checkout@v3

Cargo.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,6 +2347,7 @@ dependencies = [
23472347
"itp-utils",
23482348
"log 0.4.17",
23492349
"pallet-balances",
2350+
"pallet-jton_rps",
23502351
"parity-scale-codec",
23512352
"primitive-types",
23522353
"rand 0.8.5",
@@ -2550,6 +2551,7 @@ dependencies = [
25502551
"pallet-balances",
25512552
"pallet-evm",
25522553
"pallet-grandpa",
2554+
"pallet-jton_rps",
25532555
"pallet-parentchain",
25542556
"pallet-randomness-collective-flip",
25552557
"pallet-sudo",
@@ -2589,6 +2591,7 @@ dependencies = [
25892591
"its-state",
25902592
"log 0.4.17",
25912593
"pallet-balances",
2594+
"pallet-jton_rps",
25922595
"parity-scale-codec",
25932596
"sc-keystore",
25942597
"sgx_tstd",
@@ -4777,6 +4780,20 @@ dependencies = [
47774780
"sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)",
47784781
]
47794782

4783+
[[package]]
4784+
name = "pallet-jton_rps"
4785+
version = "0.1.0"
4786+
source = "git+https://github.com/integritee-network/pallet-jton-rps.git?branch=integritee-demo#6a8e6dcc579c32d97d4383bc2b5222eaf1f24b88"
4787+
dependencies = [
4788+
"frame-support",
4789+
"frame-system",
4790+
"parity-scale-codec",
4791+
"scale-info",
4792+
"sp-io 6.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)",
4793+
"sp-runtime",
4794+
"sp-std 4.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.26)",
4795+
]
4796+
47804797
[[package]]
47814798
name = "pallet-multisig"
47824799
version = "4.0.0-dev"

app-libs/sgx-runtime/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr
4141
sp-transaction-pool = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }
4242
sp-version = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }
4343

44+
# use case specific
45+
pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" }
46+
4447
# Integritee dependencies
4548
pallet-parentchain = { default-features = false, git = "https://github.com/integritee-network/pallets.git", branch = "master" }
4649
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 = [
7679
"pallet-balances/std",
7780
"pallet-grandpa/std",
7881
"pallet-randomness-collective-flip/std",
82+
"pallet-rps/std",
7983
"pallet-sudo/std",
8084
"pallet-timestamp/std",
8185
"pallet-transaction-payment/std",

app-libs/sgx-runtime/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ pub use frame_support::{
6363
};
6464
pub use pallet_balances::Call as BalancesCall;
6565
pub use pallet_parentchain::Call as ParentchainCall;
66+
pub use pallet_rps::Call as RpsCall;
6667
pub use pallet_timestamp::Call as TimestampCall;
68+
6769
#[cfg(any(feature = "std", test))]
6870
pub use sp_runtime::BuildStorage;
6971
pub use sp_runtime::{Perbill, Permill};
@@ -290,6 +292,10 @@ impl pallet_parentchain::Config for Runtime {
290292
type WeightInfo = ();
291293
}
292294

295+
impl pallet_rps::Config for Runtime {
296+
type Event = Event;
297+
}
298+
293299
// The plain sgx-runtime without the `evm-pallet`
294300
#[cfg(not(feature = "evm"))]
295301
construct_runtime!(
@@ -304,6 +310,7 @@ construct_runtime!(
304310
TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event<T>},
305311
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>},
306312
Parentchain: pallet_parentchain::{Pallet, Call, Storage},
313+
Rps: pallet_rps::{Pallet, Call, Storage, Event<T>},
307314
}
308315
);
309316

app-libs/stf/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ itp-storage = { default-features = false, path = "../../core-primitives/storage"
2121
its-state = { default-features = false, optional = true, path = "../../sidechain/state" }
2222
sp-io = { optional = true, default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" }
2323

24+
# use case specific
25+
pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" }
26+
2427
# Substrate dependencies
2528
sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }
2629
balances = { package = "pallet-balances", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }

app-libs/stf/src/helpers.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
1616
*/
1717
use crate::{
18-
stf_sgx_primitives::types::*, AccountId, Index, StfError, StfResult, ENCLAVE_ACCOUNT_KEY, H256,
18+
stf_sgx_primitives::types::*, AccountId, Game, Hash, Index, StfError, StfResult,
19+
ENCLAVE_ACCOUNT_KEY, H256,
1920
};
2021
use codec::{Decode, Encode};
2122
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<()> {
193194
Err(StfError::MissingPrivileges(account))
194195
}
195196
}
197+
198+
pub fn get_block_number() -> BlockNumber {
199+
get_storage_value("System", "Number").unwrap()
200+
}
201+
202+
pub fn get_game_for(who: AccountId) -> Option<Game> {
203+
if let Some(game_id) =
204+
get_storage_map::<AccountId, Hash>("Rps", "PlayerGame", &who, &StorageHasher::Identity)
205+
{
206+
if let Some(game) =
207+
get_storage_map::<Hash, Game>("Rps", "Games", &game_id, &StorageHasher::Identity)
208+
{
209+
info!("Game state for {:x?} is: {:?}", game.players, game.states);
210+
Some(game)
211+
} else {
212+
debug!("could not read game");
213+
None
214+
}
215+
} else {
216+
debug!("could not read game id");
217+
None
218+
}
219+
}

app-libs/stf/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub use my_node_runtime::{Balance, Index};
3333

3434
use codec::{Compact, Decode, Encode};
3535
use derive_more::Display;
36+
use pallet_rps::Game as GameT;
3637
use sp_core::{crypto::AccountId32, ed25519, sr25519, Pair, H256};
3738
use sp_runtime::{traits::Verify, MultiSignature};
3839
use std::string::String;
@@ -47,6 +48,8 @@ pub type ShardIdentifier = H256;
4748

4849
pub type StfResult<T> = Result<T, StfError>;
4950

51+
pub type Game = GameT<Hash, AccountId>;
52+
5053
#[derive(Debug, Display, PartialEq, Eq)]
5154
pub enum StfError {
5255
#[display(fmt = "Insufficient privileges {:?}, are you sure you are root?", _0)]
@@ -181,6 +184,9 @@ pub enum TrustedCall {
181184
balance_transfer(AccountId, AccountId, Balance),
182185
balance_unshield(AccountId, AccountId, Balance, ShardIdentifier), // (AccountIncognito, BeneficiaryPublicAccount, Amount, Shard)
183186
balance_shield(AccountId, AccountId, Balance), // (Root, AccountIncognito, Amount)
187+
rps_new_game(AccountId, AccountId),
188+
rps_choose(AccountId, pallet_rps::WeaponType),
189+
rps_reveal(AccountId, pallet_rps::WeaponType),
184190
}
185191

186192
impl TrustedCall {
@@ -190,6 +196,9 @@ impl TrustedCall {
190196
TrustedCall::balance_transfer(account, _, _) => account,
191197
TrustedCall::balance_unshield(account, _, _, _) => account,
192198
TrustedCall::balance_shield(account, _, _) => account,
199+
TrustedCall::rps_new_game(account, _) => account,
200+
TrustedCall::rps_choose(account, _) => account,
201+
TrustedCall::rps_reveal(account, _) => account,
193202
}
194203
}
195204

@@ -215,6 +224,7 @@ pub enum TrustedGetter {
215224
free_balance(AccountId),
216225
reserved_balance(AccountId),
217226
nonce(AccountId),
227+
game(AccountId),
218228
}
219229

220230
impl TrustedGetter {
@@ -223,6 +233,7 @@ impl TrustedGetter {
223233
TrustedGetter::free_balance(account) => account,
224234
TrustedGetter::reserved_balance(account) => account,
225235
TrustedGetter::nonce(account) => account,
236+
TrustedGetter::game(account) => account,
226237
}
227238
}
228239

app-libs/stf/src/stf_sgx.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::test_genesis::test_genesis_setup;
2121
use crate::{
2222
helpers::{
2323
account_data, account_nonce, enclave_signer_account, ensure_enclave_signer_account,
24-
ensure_root, get_account_info, increment_nonce, root, validate_nonce,
24+
ensure_root, get_account_info, get_game_for, increment_nonce, root, validate_nonce,
2525
},
2626
AccountData, AccountId, Getter, Index, ParentchainHeader, PublicGetter, ShardIdentifier, State,
2727
StateTypeDiff, Stf, StfError, StfResult, TrustedCall, TrustedCallSigned, TrustedGetter,
@@ -115,6 +115,12 @@ impl Stf {
115115
} else {
116116
None
117117
},
118+
TrustedGetter::game(who) =>
119+
if let Some(game) = get_game_for(who) {
120+
Some(game.encode())
121+
} else {
122+
None
123+
},
118124
},
119125
Getter::public(g) => match g {
120126
PublicGetter::some_value => Some(42u32.encode()),
@@ -200,6 +206,37 @@ impl Stf {
200206
Self::shield_funds(who, value)?;
201207
Ok(())
202208
},
209+
TrustedCall::rps_new_game(sender, opponent) => {
210+
let origin = ita_sgx_runtime::Origin::signed(sender.clone());
211+
info!("rps new_game");
212+
ita_sgx_runtime::RpsCall::<Runtime>::new_game { opponent }
213+
.dispatch_bypass_filter(origin)
214+
.map_err(|_| StfError::Dispatch("rps_new_game".to_string()))?;
215+
Ok(())
216+
},
217+
TrustedCall::rps_choose(sender, weapon) => {
218+
let origin = ita_sgx_runtime::Origin::signed(sender.clone());
219+
info!("rps choose: {:?}", weapon);
220+
ita_sgx_runtime::RpsCall::<Runtime>::choose {
221+
choice: weapon.clone(),
222+
salt: [0u8; 32],
223+
}
224+
.dispatch_bypass_filter(origin.clone())
225+
.map_err(|e| {
226+
error!("dispatch error {:?}", e);
227+
StfError::Dispatch("rps_choose".to_string())
228+
})?;
229+
Ok(())
230+
},
231+
TrustedCall::rps_reveal(sender, weapon) => {
232+
let origin = ita_sgx_runtime::Origin::signed(sender.clone());
233+
info!("rps reveal");
234+
ita_sgx_runtime::RpsCall::<Runtime>::reveal { choice: weapon, salt: [0u8; 32] }
235+
.dispatch_bypass_filter(origin)
236+
.map_err(|_| StfError::Dispatch("rps_reveal".to_string()))?;
237+
get_game_for(sender);
238+
Ok(())
239+
},
203240
}?;
204241
increment_nonce(&sender);
205242
Ok(())
@@ -304,6 +341,9 @@ impl Stf {
304341
TrustedCall::balance_transfer(_, _, _) => debug!("No storage updates needed..."),
305342
TrustedCall::balance_unshield(_, _, _, _) => debug!("No storage updates needed..."),
306343
TrustedCall::balance_shield(_, _, _) => debug!("No storage updates needed..."),
344+
TrustedCall::rps_new_game(_, _) => debug!("No storage updates needed..."),
345+
TrustedCall::rps_choose(_, _) => debug!("No storage updates needed..."),
346+
TrustedCall::rps_reveal(_, _) => debug!("No storage updates needed..."),
307347
};
308348
key_hashes
309349
}

cli/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ substrate-api-client = { features = ["ws-client"], git = "https://github.com/scs
3030
substrate-client-keystore = { git = "https://github.com/scs/substrate-api-client", branch = "polkadot-v0.9.26" }
3131
teerex-primitives = { git = "https://github.com/integritee-network/pallets.git", branch = "master" }
3232

33+
# use case specific
34+
pallet-rps = { package = "pallet-jton_rps", default-features = false, git = "https://github.com/integritee-network/pallet-jton-rps.git", branch = "integritee-demo" }
35+
3336
# substrate dependencies
3437
sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }
3538
sc-keystore = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.26" }

cli/demo_rps.sh

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# setup:
5+
# run all on localhost:
6+
# integritee-node purge-chain --dev
7+
# integritee-node --tmp --dev -lruntime=debug
8+
# rm light_client_db.bin
9+
# integritee-service init_shard
10+
# integritee-service shielding-key
11+
# integritee-service signing-key
12+
# export RUST_LOG=integritee_service=info,ita_stf=debug
13+
# integritee-service run
14+
#
15+
# then run this script
16+
17+
# usage:
18+
# demo_rps.sh -p <NODEPORT> -P <WORKERPORT> -m file
19+
20+
while getopts ":m:p:P:u:V:C:" opt; do
21+
case $opt in
22+
m)
23+
READMRENCLAVE=$OPTARG
24+
;;
25+
p)
26+
NPORT=$OPTARG
27+
;;
28+
P)
29+
WORKER1PORT=$OPTARG
30+
;;
31+
u)
32+
NODEURL=$OPTARG
33+
;;
34+
V)
35+
WORKER1URL=$OPTARG
36+
;;
37+
C)
38+
CLIENT_BIN=$OPTARG
39+
;;
40+
esac
41+
done
42+
43+
# Using default port if none given as arguments.
44+
NPORT=${NPORT:-9944}
45+
NODEURL=${NODEURL:-"ws://127.0.0.1"}
46+
47+
WORKER1PORT=${WORKER1PORT:-2000}
48+
WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"}
49+
50+
CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"}
51+
52+
READMRENCLAVE=${READMRENCLAVE:-"onchain-registry"}
53+
54+
echo "Using client binary ${CLIENT_BIN}"
55+
echo "Using node uri ${NODEURL}:${NPORT}"
56+
echo "Using trusted-worker uri ${WORKER1URL}:${WORKER1PORT}"
57+
echo "Reading MRENCLAVE from ${READMRENCLAVE}"
58+
59+
CLIENT="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1URL}"
60+
61+
if [ "$READMRENCLAVE" = "file" ]
62+
then
63+
read MRENCLAVE <<< $(cat ~/mrenclave.b58)
64+
echo "Reading MRENCLAVE from file: ${MRENCLAVE}"
65+
else
66+
# this will always take the first MRENCLAVE found in the registry !!
67+
read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }')
68+
echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}"
69+
fi
70+
[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; }
71+
72+
PLAYER1=$($CLIENT trusted --mrenclave "$MRENCLAVE" new-account)
73+
PLAYER2=$($CLIENT trusted --mrenclave "$MRENCLAVE" new-account)
74+
75+
echo "Alice (sudo) sets initial balances"
76+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct set-balance "${PLAYER1}" 1000
77+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct set-balance "${PLAYER2}" 1000
78+
echo ""
79+
80+
echo "Alice starts new game against Bob"
81+
# shellcheck disable=SC2086
82+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct new-game "${PLAYER1}" "${PLAYER2}"
83+
echo ""
84+
85+
echo "Alice chooses her weapon"
86+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct choose "${PLAYER1}" Rock
87+
echo ""
88+
89+
echo "Bob chooses his weapon"
90+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct choose "${PLAYER2}" Paper
91+
echo ""
92+
93+
echo "Alice reveals"
94+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct reveal "${PLAYER1}" Rock
95+
echo ""
96+
97+
echo "Bob reveals"
98+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct reveal "${PLAYER2}" Paper
99+
echo ""
100+
101+
echo "Query result"
102+
${CLIENT} trusted --mrenclave "${MRENCLAVE}" --direct get-game "${PLAYER1}"
103+
echo ""
104+
105+
exit 0

0 commit comments

Comments
 (0)