-
Notifications
You must be signed in to change notification settings - Fork 483
/
Copy pathClass.hs
204 lines (182 loc) · 8.16 KB
/
Class.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
-- editorconfig-checker-disable-file
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-specialise #-}
{-# OPTIONS_GHC -fno-omit-interface-pragmas #-}
module PlutusTx.IsData.Class where
import Prelude qualified as Haskell (Int, error)
import PlutusCore.Data qualified as PLC
import PlutusTx.Base
import PlutusTx.Builtins as Builtins
import PlutusTx.Builtins.Internal (BuiltinData (..))
import PlutusTx.Builtins.Internal qualified as BI
import PlutusTx.Maybe (Maybe (..))
import PlutusTx.Applicative
import PlutusTx.ErrorCodes
import PlutusTx.Trace
import Data.Kind
import Data.Void
import GHC.TypeLits (ErrorMessage (..), TypeError)
{- HLINT ignore -}
-- | A typeclass for types that can be converted to and from 'BuiltinData'.
class ToData (a :: Type) where
-- | Convert a value to 'BuiltinData'.
toBuiltinData :: a -> BuiltinData
class FromData (a :: Type) where
-- TODO: this should probably provide some kind of diagnostics
-- | Convert a value from 'BuiltinData', returning 'Nothing' if this fails.
fromBuiltinData :: BuiltinData -> Maybe a
class UnsafeFromData (a :: Type) where
-- | Convert a value from 'BuiltinData', calling 'error' if this fails.
-- This is typically much faster than 'fromBuiltinData'.
--
-- When implementing this function, make sure to call 'unsafeFromBuiltinData'
-- rather than 'fromBuiltinData' when converting substructures!
--
-- This is a simple type without any validation, __use with caution__.
unsafeFromBuiltinData :: BuiltinData -> a
instance ToData BuiltinData where
{-# INLINABLE toBuiltinData #-}
toBuiltinData = id
instance FromData BuiltinData where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData d = Just d
instance UnsafeFromData BuiltinData where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData d = d
instance (TypeError ('Text "Int is not supported, use Integer instead"))
=> ToData Haskell.Int where
toBuiltinData = Haskell.error "unsupported"
instance (TypeError ('Text "Int is not supported, use Integer instead"))
=> FromData Haskell.Int where
fromBuiltinData = Haskell.error "unsupported"
instance (TypeError ('Text "Int is not supported, use Integer instead"))
=> UnsafeFromData Haskell.Int where
unsafeFromBuiltinData = Haskell.error "unsupported"
instance ToData Integer where
{-# INLINABLE toBuiltinData #-}
toBuiltinData i = mkI i
instance FromData Integer where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData =
caseData' (\_ _ -> Nothing) (\_ -> Nothing) (\_ -> Nothing) Just (\_ -> Nothing)
instance UnsafeFromData Integer where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData = BI.unsafeDataAsI
instance ToData Builtins.BuiltinByteString where
{-# INLINABLE toBuiltinData #-}
toBuiltinData = mkB
instance FromData Builtins.BuiltinByteString where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData =
caseData' (\_ _ -> Nothing) (\_ -> Nothing) (\_ -> Nothing) (\_ -> Nothing) Just
instance UnsafeFromData Builtins.BuiltinByteString where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData = BI.unsafeDataAsB
instance ToData a => ToData [a] where
{-# INLINABLE toBuiltinData #-}
toBuiltinData l = BI.mkList (mapToBuiltin l)
where
{-# INLINE mapToBuiltin #-}
mapToBuiltin :: [a] -> BI.BuiltinList BI.BuiltinData
mapToBuiltin = go
where
go :: [a] -> BI.BuiltinList BI.BuiltinData
go [] = mkNil
go (x:xs) = BI.mkCons (toBuiltinData x) (go xs)
instance FromData a => FromData [a] where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData =
caseData'
(\_ _ -> Nothing)
(\_ -> Nothing)
traverseFromBuiltin
(\_ -> Nothing)
(\_ -> Nothing)
where
{-# INLINE traverseFromBuiltin #-}
traverseFromBuiltin :: BI.BuiltinList BI.BuiltinData -> Maybe [a]
traverseFromBuiltin = go
where
go :: BI.BuiltinList BI.BuiltinData -> Maybe [a]
go = caseList' (pure []) (\x xs -> liftA2 (:) (fromBuiltinData x) (go xs))
instance UnsafeFromData a => UnsafeFromData [a] where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData d = mapFromBuiltin (BI.unsafeDataAsList d)
where
{-# INLINE mapFromBuiltin #-}
mapFromBuiltin :: BI.BuiltinList BI.BuiltinData -> [a]
mapFromBuiltin = go
where
go :: BI.BuiltinList BI.BuiltinData -> [a]
go = caseList' [] (\x xs -> unsafeFromBuiltinData x : go xs)
instance ToData Void where
{-# INLINABLE toBuiltinData #-}
toBuiltinData = \case {}
instance FromData Void where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData _ = Nothing
instance UnsafeFromData Void where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData _ = traceError voidIsNotSupportedError
{- | For the BLS12-381 G1 and G2 types we use the `compress` functions to convert
to a ByteString and then encode that as Data as usual. We have to be more
careful going the other way because we decode a Data object to (possibly) get
a BuiltinByteString and then uncompress the underlying ByteString to get a
group element. However uncompression can fail so we have to check what
happens: we don't use bls12_381_G?_uncompress because that invokes `error` if
something goes wrong (but we do use it for unsafeFromData).
-}
instance ToData Builtins.BuiltinBLS12_381_G1_Element where
{-# INLINABLE toBuiltinData #-}
toBuiltinData = toBuiltinData . Builtins.bls12_381_G1_compress
instance FromData Builtins.BuiltinBLS12_381_G1_Element where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData d =
case fromBuiltinData d of
Nothing -> Nothing
Just bs -> Just $ bls12_381_G1_uncompress bs
instance UnsafeFromData Builtins.BuiltinBLS12_381_G1_Element where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData = Builtins.bls12_381_G1_uncompress . unsafeFromBuiltinData
instance ToData Builtins.BuiltinBLS12_381_G2_Element where
{-# INLINABLE toBuiltinData #-}
toBuiltinData = toBuiltinData . Builtins.bls12_381_G2_compress
instance FromData Builtins.BuiltinBLS12_381_G2_Element where
{-# INLINABLE fromBuiltinData #-}
fromBuiltinData d =
case fromBuiltinData d of
Nothing -> Nothing
Just bs -> Just $ bls12_381_G2_uncompress bs
instance UnsafeFromData Builtins.BuiltinBLS12_381_G2_Element where
{-# INLINABLE unsafeFromBuiltinData #-}
unsafeFromBuiltinData = Builtins.bls12_381_G2_uncompress . unsafeFromBuiltinData
{- | We do not provide instances of any of these classes for
BuiltinBLS12_381_MlResult since there is no serialisation format: we expect
that values of that type will only occur as the result of on-chain
computations.
-}
instance (TypeError ('Text "toBuiltinData is not supported for BuiltinBLS12_381_MlResult"))
=> ToData Builtins.BuiltinBLS12_381_MlResult where
toBuiltinData = Haskell.error "unsupported"
instance (TypeError ('Text "fromBuiltinData is not supported for BuiltinBLS12_381_MlResult"))
=> FromData Builtins.BuiltinBLS12_381_MlResult where
fromBuiltinData = Haskell.error "unsupported"
instance (TypeError ('Text "unsafeFromBuiltinData is not supported for BuiltinBLS12_381_MlResult"))
=> UnsafeFromData Builtins.BuiltinBLS12_381_MlResult where
unsafeFromBuiltinData = Haskell.error "unsupported"
-- | Convert a value to 'PLC.Data'.
toData :: (ToData a) => a -> PLC.Data
toData a = builtinDataToData (toBuiltinData a)
-- | Convert a value from 'PLC.Data', returning 'Nothing' if this fails.
fromData :: (FromData a) => PLC.Data -> Maybe a
fromData d = fromBuiltinData (BuiltinData d)
-- | Convert a value from 'PLC.Data', throwing if this fails.
unsafeFromData :: (UnsafeFromData a) => PLC.Data -> a
unsafeFromData d = unsafeFromBuiltinData (BuiltinData d)