· 6 years ago · Oct 05, 2019, 08:02 AM
1-- A game with two players. Player 1 thinks of a secret word
2-- and uses its hash, and the game validator script, to lock
3-- some funds (the prize) in a pay-to-script transaction output.
4-- Player 2 guesses the word by attempting to spend the transaction
5-- output. If the guess is correct, the validator script releases the funds.
6-- If it isn't, the funds stay locked.
7
8import qualified Language.PlutusTx as PlutusTx
9import Language.PlutusTx.Prelude
10import Ledger (Address, DataScript (DataScript), PendingTx,
11 RedeemerScript (RedeemerScript), ValidatorScript (ValidatorScript),
12 compileScript, lifted, plcSHA2_256, scriptAddress)
13import Ledger.Value (Value)
14import Playground.Contract
15import Wallet (MonadWallet, WalletAPI, WalletDiagnostics, collectFromScript,
16 defaultSlotRange, payToScript_, startWatching)
17
18import qualified Data.ByteString.Lazy.Char8 as C
19
20data HashedString = HashedString ByteString
21
22PlutusTx.makeLift ''HashedString
23
24data ClearString = ClearString ByteString
25
26PlutusTx.makeLift ''ClearString
27
28correctGuess :: HashedString -> ClearString -> Bool
29correctGuess (HashedString actual) (ClearString guess') =
30 actual == (sha2_256 guess')
31
32validateGuess :: HashedString -> ClearString -> PendingTx -> Bool
33validateGuess dataScript redeemerScript _ =
34 correctGuess dataScript redeemerScript
35
36-- | The validator script of the game.
37gameValidator :: ValidatorScript
38gameValidator =
39 ValidatorScript ($$(Ledger.compileScript [|| validateGuess ||]))
40
41-- create a data script for the guessing game by hashing the string
42-- and lifting the hash to its on-chain representation
43gameDataScript :: String -> DataScript
44gameDataScript =
45 DataScript . Ledger.lifted . HashedString . plcSHA2_256 . C.pack
46
47-- create a redeemer script for the guessing game by lifting the
48-- string to its on-chain representation
49gameRedeemerScript :: String -> RedeemerScript
50gameRedeemerScript =
51 RedeemerScript . Ledger.lifted . ClearString . C.pack
52
53-- | The address of the game (the hash of its validator script)
54gameAddress :: Address
55gameAddress = Ledger.scriptAddress gameValidator
56
57-- | The "lock" contract endpoint. See note [Contract endpoints]
58lock :: MonadWallet m => String -> Value -> m ()
59lock word vl =
60 -- 'payToScript_' is a function of the wallet API. It takes a script
61 -- address, a currency value and a data script, and submits a transaction
62 -- that pays the value to the address, using the data script.
63 --
64 -- The underscore at the end of the name indicates that 'payToScript_'
65 -- discards its result. If you want to hold on to the transaction you can
66 -- use 'payToScript'.
67 payToScript_ defaultSlotRange gameAddress vl (gameDataScript word)
68
69-- | The "guess" contract endpoint. See note [Contract endpoints]
70guess :: (WalletAPI m, WalletDiagnostics m) => String -> m ()
71guess word = do
72 let redeemer = gameRedeemerScript word
73 -- 'collectFromScript' is a function of the wallet API. It consumes the
74 -- unspent transaction outputs at a script address and pays them to a
75 -- public key address owned by this wallet. It takes the validator script
76 -- and the redeemer scripts as arguments.
77 --
78 -- Note that before we can use 'collectFromScript', we need to tell the
79 -- wallet to start watching the address for transaction outputs (because
80 -- the wallet does not keep track of the UTXO set of the entire chain).
81 collectFromScript defaultSlotRange gameValidator redeemer
82
83-- | The "startGame" contract endpoint, telling the wallet to start watching
84-- the address of the game script. See note [Contract endpoints]
85startGame :: MonadWallet m => m ()
86startGame =
87 -- 'startWatching' is a function of the wallet API. It instructs the wallet
88 -- to keep track of all outputs at the address. Player 2 needs to call
89 -- 'startGame' before Player 1 uses the 'lock' endpoint, to ensure that
90 -- Player 2's wallet is aware of the game address.
91 startWatching gameAddress
92
93$(mkFunctions
94 ['lock
95 , 'guess
96 , 'startGame
97 ])
98
99{- Note [Contract endpoints]
100
101A contract endpoint is a function that uses the wallet API to interact with the
102blockchain. We can look at contract endpoints from two different points of view.
103
1041. Contract users
105
106Contract endpoints are the visible interface of the contract. They provide a
107UI (HTML form) for entering the parameters of the actions we may take as part
108of the contract.
109
1102. Contract authors
111
112As contract authors we define endpoints as functions that return a value of
113type 'MockWallet ()'. This type indicates that the function uses the wallet API
114to produce and spend transaction outputs on the blockchain.
115
116Endpoints can have any number of parameters: 'lock' has two
117parameters, 'guess' has one and 'startGame' has none. For each endpoint we
118include a call to 'mkFunction' at the end of the contract definition. This
119causes the Haskell compiler to generate a schema for the endpoint. The Plutus
120Playground then uses this schema to present an HTML form to the user where the
121parameters can be entered.
122
123-}
124myCurrencies :: KnownCurrency
125myCurrencies = KnownCurrency "b0b0" "MyCurrency" ("USDToken" :| ["EURToken"])
126
127$(mkKnownCurrencies ['myCurrencies])