1

In the firstGame function, we use the "logging" component of the contract to tell the thread token. It's not obvious to me where this is needed: is there an internal check in the runStep function that this token matches the one in the parameter, i.e. SecondParams? I commented out the line where tt is told and that results in an endless loop, while I expected a failure.

1 Answer 1

2

Because firstGame is the contract that initialises the game, it needs to communicate somehow the generated threadToken to the external world (secondGame in this case). For this kind of communication Contract monad uses tell mechanism.

How does the info flow? You can see it in myTrace function in Week07.TestStateMachine.

  • (1) callEndpoint @"first" h1 fp - Initialise the game, and firstGame contract internally tells the generated threadToken to anyone interested in it.

  • (2) tt <- getTT h1 - This function uses observableState function, which "get the observable state w of a contract instance". In our case w in firstGame Contract is (Last ThreadToken).

   firstGame :: forall s. FirstParams -> Contract (Last ThreadToken) s Text ()
  • (3) let sp = SecondParams {... spToken = tt ...} - Build params for secondGame using threadToken generated by firstGame contract, tt.

  • (4) callEndpoint @"second" h2 sp - secondGame plays.

Notice that communication is not direct Contract to Contract but through observableState function

myTrace :: GameChoice -> GameChoice -> EmulatorTrace ()
myTrace c1 c2 = do
    Extras.logInfo $ "first move: " ++ show c1 ++ ", second move: " ++ show c2

    let w1 = knownWallet 1
    let w2 = knownWallet 2

    h1 <- activateContractWallet w1 endpoints
    h2 <- activateContractWallet w2 endpoints

    let pkh1      = mockWalletPaymentPubKeyHash w1
        pkh2      = mockWalletPaymentPubKeyHash w2
        stake     = 5_000_000
        deadline1 = slotToEndPOSIXTime def 5
        deadline2 = slotToEndPOSIXTime def 10

        fp = FirstParams
                { fpSecond         = pkh2
                , fpStake          = stake
                , fpPlayDeadline   = deadline1
                , fpRevealDeadline = deadline2
                , fpNonce          = "SECRETNONCE"
                , fpChoice         = c1
                }

    callEndpoint @"first" h1 fp   -- (1)

    tt <- getTT                   -- (2)

    let sp = SecondParams         -- (3)
                { spFirst          = pkh1
                , spStake          = stake
                , spPlayDeadline   = deadline1
                , spRevealDeadline = deadline2
                , spChoice         = c2
                , spToken          = tt
                }

    void $ Emulator.waitNSlots 3  -- (4)

    callEndpoint @"second" h2 sp

    void $ Emulator.waitNSlots 10
  where
    getTT :: ContractHandle (Last ThreadToken) GameSchema Text -> EmulatorTrace ThreadToken
    getTT h = do
        void $ Emulator.waitNSlots 1
        Last m <- observableState      -- (2)
        case m of
            Nothing -> getTT h
            Just tt -> Extras.logInfo ("read thread token " ++ show tt) >> return tt
1
  • Ouch, my bad, I didn't look at the test. The getTT method also explains the loop when I comment out the tell. Commented Mar 4, 2022 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.