This repository is the continued work of tic-tac-toe game on Chia Blockchain from chia-concepts/tic-tac-toe.
In the chia-concepts/tic-tac-toe, in order to create an initial singleton game coin, both Alice and Bob have to be able to sign and spend their coins together.
The process is cumbersome and requires Bob to trust Alice not to steal his coin (because Bob has to provide his signature for his coin spend). Also, both Alice and Bob can't use standard wallets and have to prepare conditions for the standard spend manually.
# alice's coin creates a launcher coin
alice_conditions = [
# create launcher coin with the odd_amount (odd)
Program.to(
[
ConditionOpcode.CREATE_COIN,
singleton_top_layer_v1_1.SINGLETON_LAUNCHER_HASH,
game_amount,
]),
# assert launcher coin announcement
Program.to(
[
ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
std_hash(launcher_id + launcher_announcement)
]),
# change
[ConditionOpcode.CREATE_COIN, alice.puzzle_hash, alice_coin.amount - (player_amount + 1)]
]
# bob's coin create change back to himself
bob_conditions = [
[ConditionOpcode.CREATE_COIN, bob.puzzle_hash, bob_coin.amount - player_amount],
]
To resolve the issue, we introduced the waiting room puzzle allowing both Alice and Bob to send XCH to create their waiting room coins using their standard wallets.
Once the waiting room coins for both players are on the blockchain. Alice can create a spend bundle by spending two waiting room coins to create a Tic Tac Toe game coin.
If either Alice or Bob or both decide not to play the game (e.g., only one waiting room coin is created or both waiting room coins are created, but Alice doesn't spend them to create a game coin), players can claw back their XCH.
Each waiting coin also curries in the RETURN_AMOUNT
(for clawback) and GAME_AMOUNT
(amount to get when someone wins) that are agreed upon before creating the game.
; P1_PK : Player One PK
; P2_PK : Player Two PK
; RETURN_AMOUNT : Amount returned to player when clawback in mojos
; GAME_AMOUNT : Odd game amount in mojos
; P1_PH : P1 coin puzzle hash, null if this is P1 coin
; clawback_puzzle_hash : Clawback Puzzle Hash, null if not clawback
; launcher_coin_announcement : Expected coin announcement from the launcher coin, null if Clawback
sequenceDiagram;
actor A as Alice;
actor B as Bob;
participant a as Alice's Coin;
participant b as Bob's Coin;
participant s as Singleton Launcher
participant t as Tic Tac Toe Coin;
rect rgb(220, 220, 220);
note over A,B: (1) PKs Exchanging;
A-)B: Alice's PK;
B-)A: Bob's PK;
end;
rect rgb(191, 223, 255);
note over a,t: (4) Alice's and Bob's coin are spent and a singleton launcer is created and spent;
A->>a: (2) Create Alice's Coin;
activate a
B->>b: (3) Create Bob's Coin;
activate b
a->>s: CREATE_COIN
deactivate a
deactivate b
activate s
s->>t: CREATE_COIN
deactivate s
end;
- Alice (P1) and Bob (P2) exchange their PKs.
- Alice creates her waiting room coin (Alice's coin) with her
pk
, bob'spk
, agreedRETURN_AMOUNT
andGAME_AMOUNT
, andnull
forP1_PH
. - Bob creates his waiting room coin (Bob's coin) with Alice's
pk
, hispk
, agreedRETURN_AMOUNT
andGAME_AMOUNT
, and calculated Alice's coin puzzle hash forP1_PH
. - Once two coins with Alice's and Bob's waiting room puzzles are created, Alice's and Bob's coin spends can be spent together in a spend bundle to create the tic tac toe singleton coin. Alice's coin creates an ephermeral launcher coin that creates the game coin. Bob's coin is burned.
The waiting room puzzle for Alice allows the clawback after 100 blocks has passed if Bob has not created his waiting room coin. Or if Bob has created his coin, but Alice clawed back her XCH and didn't create a game.
sequenceDiagram;
actor A as Alice;
actor B as Bob;
participant a as Alice's Coin;
rect rgb(220, 220, 220);
note over A,B: (1) PKs Exchanging;
A-)B: Alice's PK;
B-)A: Bob's PK;
end;
rect rgb(191, 223, 255);
note over A,a: Bob doesn't create his coin, so Alice waits for 100 blocks and claw back her XCH
A->>a: (2) Create Alice's Coin;
activate a
loop (3) Alice Waits for Bob to Create His Coin
A->>B: Wait Until 100 Blocks Have Passed;
end
a->>A: (4) Alice Spends Her Coin To Claw back Her XCH;
deactivate a
end;
- Alice (P1) and Bob (P2) exchange their PKs.
- Alice creates her waiting room coin (Alice's coin) with her
pk
, bob'spk
, agreedRETURN_AMOUNT
andGAME_AMOUNT
, andnull
forP1_PH
. - Alice waits for Bob to create his waiting room coin.
- After 100 blocks have passed, Alice can spend her waiting room coin to claw back her XCH.
Alice's, Bob's, and the launcher coin have to be spent together in one transaction to create a singleton tic-tac-toe game coin.
flowchart TB
classDef waiting_room fill:#87CEFA;
classDef coin_announcement fill:#FFA07A, stroke-dasharray: 5 5;
classDef puzzle_announcement fill:#CD5C5C, stroke-dasharray: 5 5;
subgraph Atomic Transaction
a((Alice's)):::waiting_room-->|CREATE_PUZZLE_ANNOUNCEMENT|aca[/Alice's Puzzle Anouncement/]:::puzzle_announcement
b((Bob's)):::waiting_room-->|ASSERT_PUZZLE_ANNOUNCEMENT|aca
a==>|CREATE_COIN|l((Launcher))
l-->|CREATE_COIN_ANNOUNCEMENT|lca[/Launcher Coin Anouncement/]:::coin_announcement
a-->|ASSERT_COIN_ANNOUNCEMENT|lca
end
style t0 fill:#FFC0CB,stroke:#FF69B4,stroke-width:2px
l==>|CREATE_COIN|t0((Tic-Tac-Toe 0))
- When Alice's coin is spent, the launcher coin is created and spent in the same transaction. The launcher coin also creates the game coin and generates the coin announcement. Alice's coin then asserts the launcher coin announcement.
(list ASSERT_COIN_ANNOUNCEMENT launcher_coin_announcement)
- Alice provides
launcher_coin_announcement
in a solution and sign it with her secret key.
alice_waiting_room_coin_spend = CoinSpend(
alice_waiting_room_coin,
alice_waiting_room_puzzle,
Program.to([
0, # null if not clawback
launcher_coin_announcement
])
)
...
message: bytes = launcher_coin_announcement
sig_alice_spend: G2Element = AugSchemeMPL.sign(
alice_sk,
message
+ alice_waiting_room_coin.name()
+ DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA
)
- Alice's coin verifies the signature.
(list AGG_SIG_ME P1_PK launcher_coin_announcement)
- Both Alice's and Bob's coins are curried in both players' PKs and agreed amounts which is also provided to the launcher coin as Key/Value. A tree hash of the launcher solution is
launcher_announcement
which will be asserted when all coins are spent. - Key/Value List for the launcher coin:
launcher_solution = Program.to(
[
singleton_puzzle.get_tree_hash(),
game_amount,
[
("game", "tic-tac-toe"),
("p1_pk", alice_pk),
("p2_pk", bob_pk),
("return_amount", play_amount),
("game_amount", game_amount)
]
]
)
launcher_announcement = launcher_solution.get_tree_hash()
- Bob's coin is curried with Alice's coin puzzle hash (
P1_PH
). Bob can calculate theP1_PH
himself. - Bob's coin also asserts the puzzle announcement of the launcher coin announcement announced by Alice's coin.
; P2 asserts a puzzle announcement from P1
; We want to secure P2 spend without AGG_SIG_ME
(list ASSERT_PUZZLE_ANNOUNCEMENT (sha256 P1_PH launcher_coin_announcement))
- In Clawback case, a player has to sign their
clawback_puzzle_hash
and thereturn amount
with their secret key to claw back their XCH. The clawback case is valid only after 100 blocks have passed after the waiting room coin is created.
(defconstant CLAWBACK_BLOCKS 100)
...
(defun-inline clawback (RETURN_AMOUNT P1_PH clawback_puzzle_hash)
(list
(list ASSERT_HEIGHT_RELATIVE CLAWBACK_BLOCKS) ; clawback condition valid after CLAWBACK_BLOCKS blocks have passed
(list CREATE_COIN clawback_puzzle_hash RETURN_AMOUNT) ; return mojos to provided puzzle hash
(list
AGG_SIG_ME
(if P1_PH
P2_PK
P1_PK ; null if this is P1 coin
)
(sha256 clawback_puzzle_hash RETURN_AMOUNT))
)
)