IntersectMBO / plutus

The Plutus language implementation and tools
Apache License 2.0
1.57k stars 480 forks source link

Cannot compare TokenName in Validator #3254

Closed tdiesler closed 3 years ago

tdiesler commented 3 years ago

Area

[] Plutus Foundation Related to the GHC plugin, Haskell-to-Plutus compiler, on-chain code

Summary

Simple equality of a token name does not compile

image
GHC Core to PLC plugin: E043:Error: Reference to a name which is not a local, a builtin, or an external INLINABLE function: Variable GHC.Magic.runRW#
            No unfolding
Context: Compiling expr: GHC.Magic.runRW#
Context: Compiling expr: GHC.Magic.runRW#
                           @ ('GHC.Types.TupleRep
                                '[ 'GHC.Types.TupleRep '[], 'GHC.Types.LiftedRep])
Context: Compiling expr: GHC.Magic.runRW#
                           @ ('GHC.Types.TupleRep
                                '[ 'GHC.Types.TupleRep '[], 'GHC.Types.LiftedRep])
                           @ (# GHC.Prim.State# GHC.Prim.RealWorld, Data.Text.Internal.Text #)
Context: Compiling expr: GHC.Magic.runRW#
                           @ ('GHC.Types.TupleRep
                                '[ 'GHC.Types.TupleRep '[], 'GHC.Types.LiftedRep])
                           @ (# GHC.Prim.State# GHC.Prim.RealWorld, Data.Text.Internal.Text #)
                           (\ (s1 [Occ=Once, OS=OneShot]
                                 :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                              case GHC.Prim.newByteArray# @ GHC.Prim.RealWorld 10# s1 of
                              { (# ipv [Occ=Once], ipv1 [Occ=Once] #) ->
                              Plutus.V1.Ledger.Value.$fIsStringTokenName3
                                (Data.Text.Array.MArray @ GHC.Prim.RealWorld ipv1)
                                Plutus.V1.Ledger.Value.$fIsStringTokenName2
                                dt
                                Plutus.V1.Ledger.Value.$fFromJSONAssetClass2
                                ipv
                              })

System info (please complete the following information):

tdiesler commented 3 years ago

I'm generally having a hard time comparing variants of string

-- GHC Core to PLC plugin: E043:Error: Reference to a name which is not a local, a builtin, or an external INLINABLE function: Variable GHC.List.$wlenAcc
("abc" :: ByteString) == ("abc" :: ByteString)

-- No instance for (Eq Char) arising from a use of ‘==’
("abc" :: String) == ("abc" :: String)

-- No instance for (Eq Text) arising from a use of ‘==’
("abc" :: Text) == ("abc" :: Text)
ghost commented 3 years ago
  1. Please, make sure that you use PlutusTx.== instead of Prelude.==.

TokenName is a newtype around Builtins.ByteString which has PlutusTx.Eq instance.

  1. -- GHC Core to PLC plugin: E043:Error: Reference to a name which is not a local, a builtin, or an external INLINABLE function: Variable GHC.List.$wlenAcc ("abc" :: ByteString) == ("abc" :: ByteString)

This error happens because ByteString literals are not supported yet (#3156). Will be fixed in the future.

3.

-- No instance for (Eq Char) arising from a use of ‘==’ ("abc" :: String) == ("abc" :: String)

The error happens here because there is no instance PlutusTx.Eq for Prelude.Char since String = [Char] in Prelude. And there is no way to compare two String's yet. It should be implemented in the future. Thanks for raising the priority of that!

4.

-- No instance for (Eq Text) arising from a use of ‘==’ ("abc" :: Text) == ("abc" :: Text)

Yeah, Text is unsupported (there is no instance for PlutusTx.Eq class. Only ByteString and String.

tdiesler commented 3 years ago

I have ...

import qualified PlutusTx               as PlutusTx
import           PlutusTx.Prelude       hiding (Semigroup(..))
import           Prelude                (Semigroup (..))

and later ...

image

This gives me ...

image

Could you perhaps show me how to construct a TokenName from a String and an Integer and then compare that to the TokenName that the caller provided?

The code is here ... https://github.com/tdiesler/plutus-pioneer-program/blob/pl3246b/code/payout/src/Astor/Payout.hs#L287

ghost commented 3 years ago

== is exported by PlutusTx.Prelude, not PlutusTx.

So it should be:

import qualified PlutusTx.Prelude       as Plutus
import           Prelude                (Semigroup (..))

...

Plutus.== 

TokenName is constructed by calling TokenName constructor (from Ledger module) applied to ByteString:

exptn :: TokenName
exptn = TokenName "Astor260"

"Astor260" here should be ByteString.

Could you perhaps show me how to construct a TokenName from a String and an Integer and then compare that to the TokenName that the caller provided?

It's not possible yet unfortunately. There is no way to transform integer into String or ByteString.

You may try to declare a pair (ByteString, Integer) and compare tuples.

tdiesler commented 3 years ago

Thanks. Could I perhaps extract the Integer suffix from the caller provided TokenName? For example, everything after the first five bytes should read as an Integer.

ghost commented 3 years ago

Well, there is dropByteString that allows to get the suffix as a ByteString. And then you can compare it to other ByteStrings.

tdiesler commented 3 years ago

I have a 260 :: Integer and need to validate that the caller provides an "Astor260" :: TokenName.

I would either need to turn the TokenName suffix into an Integer and compare the two Integers or the Integer that I have into a BytesString and then compare those. From what we said above, it is not (yet) possible construct a TokenName from a String prefix plus Integer - like this ("Astor" ++ (show 260)) :: TokenName so that I could simple compare two TokenNames.

How would you validate the given TokenName?

ghost commented 3 years ago

Again, it's not possible to convert Integer to any string yet in on-chain code.

You can take the suffix and parse it for example:

let suffix = dropByteString 5 "Astor260" in
case suffix of
  "260" -> handleTheSituationWhenSuffixIs260
  _ -> ...
tdiesler commented 3 years ago

How about this ...

image

I can construct the token name from String+Integer in off-chain code and put it in the Datum. In on-chain code, I now have both, the epoch Integer and the associated TokenName (which I cannot construct in on-chain code).

tdiesler commented 3 years ago

merci