|
| 1 | +module Main (main) where |
| 2 | + |
| 3 | +import Data.Text (Text) |
| 4 | +import Data.Text.Encoding qualified as Text |
| 5 | +import LambdaBuffers.Example.Plutarch ( |
| 6 | + Content (Content'Text), |
| 7 | + Message (Message), |
| 8 | + Ref (Ref), |
| 9 | + Status (Status'Active), |
| 10 | + User (User), |
| 11 | + ) |
| 12 | +import LambdaBuffers.Plutus.V1.Plutarch (Bytes, POSIXTime) |
| 13 | +import LambdaBuffers.Prelude.Plutarch () |
| 14 | +import LambdaBuffers.Runtime.Plutarch (PList (PList)) |
| 15 | +import LambdaBuffers.Runtime.Plutarch qualified as Lb |
| 16 | +import Plutarch (ClosedTerm, Config (Config), PlutusType, Term, TracingMode (DoTracingAndBinds), compile, pcon, perror, plam, pmatch, unTermCont, (#), (:-->)) |
| 17 | +import Plutarch.Api.V1 (PCurrencySymbol (PCurrencySymbol), PTokenName (PTokenName), ptuple) |
| 18 | +import Plutarch.Api.V1.Time (PPOSIXTime (PPOSIXTime)) |
| 19 | +import Plutarch.ByteString (PByteString) |
| 20 | +import Plutarch.Evaluate (evalScript) |
| 21 | +import Plutarch.Extra.TermCont (pletC, pmatchC) |
| 22 | +import Plutarch.Maybe qualified as Scott |
| 23 | +import Plutarch.Prelude (PAsData, PBool (PFalse, PTrue), PBuiltinList, PEq ((#==)), PIsData, pconstant, pdata, pfind, pfromData, pif, pshow, ptrace, (#&&)) |
| 24 | + |
| 25 | +userRef :: Text -> Term s (Ref User) |
| 26 | +userRef userName = userRef' (pfromData $ name userName) |
| 27 | + |
| 28 | +userRef' :: Term s Bytes -> Term s (Ref User) |
| 29 | +userRef' userName = pcon $ Ref (pdata $ ptuple # pcon' (PCurrencySymbol (pconstant "users")) # pcon' (PTokenName userName)) |
| 30 | + |
| 31 | +activeUser :: Text -> [Term s (Ref User)] -> Integer -> Term s User |
| 32 | +activeUser n friends since = pcon $ User (name n) (pdata $ activeSince since) (pdata $ Lb.plistFrom friends) |
| 33 | + |
| 34 | +activeSince :: Integer -> Term s Status |
| 35 | +activeSince since = pcon (Status'Active (pcon' $ PPOSIXTime (pconstant since))) |
| 36 | + |
| 37 | +name :: Text -> Term s (PAsData PByteString) |
| 38 | +name = textToBytes |
| 39 | + |
| 40 | +message :: Term s POSIXTime -> Term s (Ref User) -> Term s (Ref User) -> Term s Content -> Term s Message |
| 41 | +message at from to content = pcon $ Message (pdata at) (pdata from) (pdata to) (pdata content) |
| 42 | + |
| 43 | +-- | `isFriendly users msg` checks whether a "'sup" message is exchanged between friends. |
| 44 | +isFriendly :: Term s (Lb.PList User :--> Message :--> PBool) |
| 45 | +isFriendly = plam $ \users msg -> unTermCont $ do |
| 46 | + Message _at from to content <- pmatchC msg |
| 47 | + PList users' <- pmatchC users |
| 48 | + User fromName _ fromFriends <- pmatchC (pfromData $ findUserOrError # users' # pfromData from) |
| 49 | + User toName _ toFriends <- pmatchC (pfromData $ findUserOrError # users' # pfromData to) |
| 50 | + pletC $ |
| 51 | + pif |
| 52 | + ( (isFriend # fromFriends # toName) |
| 53 | + #== (isFriend # toFriends # fromName) |
| 54 | + #&& (content #== pcon' (Content'Text (textToBytes "'sup"))) |
| 55 | + ) |
| 56 | + (pcon PTrue) |
| 57 | + (ptrace ("This wasn't a friendly message :(" <> pshow msg) perror) |
| 58 | + where |
| 59 | + findUser :: Term s (PBuiltinList (PAsData User) :--> Ref User :--> Scott.PMaybe (PAsData User)) |
| 60 | + findUser = plam $ |
| 61 | + \users uRef -> |
| 62 | + pfind |
| 63 | + # plam (\u -> pmatch (pfromData u) (\(User userName _userActiveSince _userFriends) -> userRef' (pfromData userName) #== uRef)) |
| 64 | + # users |
| 65 | + |
| 66 | + findUserOrError :: Term s (PBuiltinList (PAsData User) :--> Ref User :--> PAsData User) |
| 67 | + findUserOrError = plam $ |
| 68 | + \users uRef -> |
| 69 | + pmatch |
| 70 | + (findUser # users # uRef) |
| 71 | + $ \case |
| 72 | + Scott.PJust uName -> uName |
| 73 | + Scott.PNothing -> ptrace ("Error while finding a user with reference " <> pshow uRef <> " amongst given users " <> pshow users) perror |
| 74 | + |
| 75 | + isFriend :: Term s (PAsData (Lb.PList (Ref User)) :--> (PAsData Bytes :--> PBool)) |
| 76 | + isFriend = plam $ \friends uname -> |
| 77 | + pmatch |
| 78 | + (pfind # plam (\friendRef -> pdata (userRef' (pfromData uname)) #== friendRef) # (toBuiltinList # pfromData friends)) |
| 79 | + ( \case |
| 80 | + Scott.PJust _ -> pcon PTrue |
| 81 | + _ -> pcon PFalse |
| 82 | + ) |
| 83 | + |
| 84 | +-- | Utils |
| 85 | +pcon' :: PIsData a => PlutusType a => a s -> Term s (PAsData a) |
| 86 | +pcon' = pdata . pcon |
| 87 | + |
| 88 | +textToBytes :: Text -> Term s (PAsData PByteString) |
| 89 | +textToBytes = pdata . pconstant . Text.encodeUtf8 |
| 90 | + |
| 91 | +toBuiltinList :: Term s (Lb.PList a :--> PBuiltinList (PAsData a)) |
| 92 | +toBuiltinList = plam $ \xs -> pmatch xs (\(Lb.PList xs') -> xs') |
| 93 | + |
| 94 | +evalBool :: ClosedTerm PBool -> IO () |
| 95 | +evalBool t = |
| 96 | + case Plutarch.compile (Config DoTracingAndBinds) (pif t (pcon PTrue) (ptrace "Term evaluated to False" perror)) of |
| 97 | + Left err -> print ("Error while compiling a Plutarch Term" :: String, err) |
| 98 | + Right script -> case evalScript script of |
| 99 | + (Left err, _, trace) -> print ("Not a friendly message it seems" :: String, err, trace) |
| 100 | + _ -> print ("Friends, peace and love!!!" :: String) |
| 101 | + |
| 102 | +-- | Main program |
| 103 | +drazen :: Term s User |
| 104 | +drazen = activeUser "Drazen Popovic" [userRef "Gergely Szabó", userRef "Jared Pon"] 0 |
| 105 | + |
| 106 | +gergo :: Term s User |
| 107 | +gergo = activeUser "Gergely Szabó" [userRef "Jared Pon", userRef "Drazen Popovic"] 1 |
| 108 | + |
| 109 | +jared :: Term s User |
| 110 | +jared = activeUser "Jared Pon" [userRef "Gergely Szabó", userRef "Drazen Popovic"] 2 |
| 111 | + |
| 112 | +supJaredSaidGergo :: Term s Message |
| 113 | +supJaredSaidGergo = message (pcon $ PPOSIXTime (pconstant 10)) (userRef "Gergely Szabó") (userRef "Jared Pon") (pcon $ Content'Text (textToBytes "'sup")) |
| 114 | + |
| 115 | +main :: IO () |
| 116 | +main = evalBool $ isFriendly # Lb.plistFrom [drazen, gergo, jared] # supJaredSaidGergo |
0 commit comments