1

Spoiler Alert!

The solution for lesson 3 includes the following:

{-# INLINABLE mkValidator #-}
-- This should validate if either beneficiary1 has signed the transaction and the current slot is before or at the deadline
-- or if beneficiary2 has signed the transaction and the deadline has passed.
mkValidator :: VestingDatum -> () -> ScriptContext -> Bool
mkValidator dat () ctx
    | (unPaymentPubKeyHash (beneficiary1 dat) `elem` sigs) && (to       (deadline dat) `contains` range) = True
    | (unPaymentPubKeyHash (beneficiary2 dat) `elem` sigs) && (from (1 + deadline dat) `contains` range) = True
    | otherwise = False
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx
sigs :: [PubKeyHash]
sigs = txInfoSignatories info

range :: POSIXTimeRange
range = txInfoValidRange info

It appears that " txInfoSignatories " is a function (functor?) that analyzes " info " which itself is drawn from the script context, but I am at a loss as to where the " txInfoSignatories " code actually comes from. Is there a way to analyze which imported module it is attached to? Is there a way to analyze its code in order to understand what it does? Much of what I am seeing in Haskell seems so opaque, with new features simply appearing and magically doing something, but I am lost as to how I could have analyzed the code/modules/options and come up with this solution on my own.

For the record, here is what I created before I looked at the solution. It compiles without error, but does not behave in the playground as intended.

{-# INLINABLE mkValidator #-}
-- This should validate if either beneficiary1 has signed the transaction and the current slot is before or at the deadline
-- or if beneficiary2 has signed the transaction and the deadline has passed.
mkValidator :: VestingDatum -> () -> ScriptContext -> Bool
-- mkValidator _ _ _ = False -- FIX ME!
mkValidator dat () ctx = 
     ( traceIfFalse "beneficiary 1 - signature missing" signedByBeneficiary1 
    && traceIfFalse "deadline 1 has passed" deadline1Reached )
    || (traceIfFalse "beneficiary 2 - signature missing" signedByBeneficiary2 
    && traceIfFalse "deadline 2 not reached" deadline2Reached )
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx
signedByBeneficiary1 :: Bool
signedByBeneficiary1 = txSignedBy info $ unPaymentPubKeyHash $ beneficiary1 dat

signedByBeneficiary2 :: Bool
signedByBeneficiary2 = txSignedBy info $ unPaymentPubKeyHash $ beneficiary2 dat

deadline1Reached :: Bool
deadline1Reached = contains (to $ deadline dat) $ txInfoValidRange info
deadline2Reached :: Bool
deadline2Reached = contains (from $ deadline dat) $ txInfoValidRange info

Any Haskell wisdom is much appreciated. I feel like this is more a Haskell knowledge deficit than anything else.

1 Answer 1

3

TxInfo is a Record type and one of the fields of the TxInfo-record is txInfoSignatories. The fields in the records work as "getters". You may refer e.g. to learnyouhaskell-records for an example.

The whole definition for TxInfo is here:

TxInfo

It is also completely fine to use the txSignedBy helper function as you have done to check whether the given hash is one of the signatories.

6
  • Your answer simply moves my question from txInfoSignatories to TxInfo. Where does TxInfo come from? It is not defined in the code for Solution1.hs. If I (in the repl) do ":i TxInfo" or ":t TxInfo" or simply "TxInfo" I get a "variable not in scope" error message. Where does that definition (a very nice and helpful definition to be sure) for TxInfo come from, and how could I have found it on my own?
    – XiTouch
    Jan 31, 2022 at 21:31
  • 2
    It is one of the fields of the ScriptContext ctx, see "info :: TxInfo info = scriptContextTxInfo ctx". In repl, do "import Ledger" then ":i TxInfo" and ":i ScriptContext" should work. Jan 31, 2022 at 22:42
  • Thank you @MihaiCodescu, I see that now. Is there a way to quickly trace/track these nested types/data/functions/records or is a tedious hunting mission necessary when reverse engineering? BTW, I discovered the :browse command in the repl, which does help.
    – XiTouch
    Jan 31, 2022 at 23:50
  • @XiTouch It also helps to remember that the validators in the extended UTXO model "see" the whole transaction they are about to validate. In other words: the context where the script is run contains full transaction info. Thus, it is not a big surprise to find TxInfo within ScriptContext. Feb 1, 2022 at 5:32
  • 1
    I spent a day creating this animation to visualize the connections and sources of each part. Hope this helps anyone else. youtube.com/watch?v=ZLURnogC_kU
    – XiTouch
    Feb 2, 2022 at 15:51

Your Answer

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

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