morley-1.5.0: src/Morley/Micheline/Class.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
-- | Module that provides type classes for converting to and from low-level
-- Micheline representation.
module Morley.Micheline.Class
( ToExpression (..)
, FromExpressionError (..)
, FromExpression (..)
) where
import qualified Data.ByteString.Lazy as LBS
import Data.Sequence (fromList, (|>))
import Data.Singletons (pattern FromSing, Sing, SingI, withSingI)
import Fmt (Buildable(..), pretty)
import Michelson.Interpret.Pack (encodeValue', packCode', packNotedT', packT')
import Michelson.Interpret.Unpack (UnpackError, decodeContract, unpackInstr', unpackValue')
import Michelson.Typed
(Contract(..), HasNoOp, Instr(..), Notes(..), T(..), Value, pnNotes, pnRootAnn)
import Michelson.Typed.Instr (mapEntriesOrdered)
import Michelson.Typed.Scope (UnpackedValScope)
import qualified Michelson.Untyped as Untyped
import Michelson.Untyped.Annotation (RootAnn, convAnn, noAnn)
import Michelson.Untyped.Instr (ExpandedOp)
import Morley.Micheline.Binary (decodeExpression, encodeExpression)
import Morley.Micheline.Expression
(Annotation(..), Expression(..), MichelinePrimAp(..), MichelinePrimitive(..))
import Util.Binary (launchGet)
-- | Type class that provides an ability to convert
-- something to Micheline Expression.
class ToExpression a where
toExpression :: a -> Expression
instance ToExpression (Instr inp out) where
toExpression = decodeExpression . packCode'
instance ToExpression T where
toExpression (FromSing (ts :: Sing t)) =
decodeExpression $ withSingI ts $ (packT' @t)
instance SingI t => ToExpression (Notes t) where
toExpression = decodeExpression . packNotedT'
instance (SingI t, HasNoOp t) => ToExpression (Value t) where
toExpression = decodeExpression . encodeValue'
instance ToExpression (Contract cp st) where
toExpression contract@Contract{..} = ExpressionSeq $ fromList $ mapEntriesOrdered contract
(\param -> ExpressionPrim $
MichelinePrimAp (MichelinePrimitive "parameter")
(fromList [ addRootAnnToExpression (pnRootAnn param) $
toExpression $ pnNotes param
])
(fromList [])
)
(\store -> ExpressionPrim $
MichelinePrimAp (MichelinePrimitive "storage")
(fromList [toExpression $ store])
(fromList [])
)
(\code -> ExpressionPrim $
MichelinePrimAp (MichelinePrimitive "code")
(fromList [toExpression code])
(fromList [])
)
where
addRootAnnToExpression :: HasCallStack =>
RootAnn -> Expression -> Expression
addRootAnnToExpression rootAnn expr = case expr of
ExpressionPrim p
| rootAnn /= noAnn -> ExpressionPrim p
{ mpaAnnots = mpaAnnots p |>
(AnnotationField $ convAnn rootAnn)
}
| otherwise -> expr
-- Currently this error can't happen because parameter type
-- must be a Micheline primitive. If it ever changes, we
-- would like to notice it ASAP and update this place.
_ -> error $ "parameter is not a primitive: " <> show expr
-- | Errors that can happen when we convert an 'Expression' to our
-- data type.
data FromExpressionError = FromExpressionError UnpackError
deriving stock (Show, Eq)
instance Buildable FromExpressionError where
build (FromExpressionError err) = build err
instance Exception FromExpressionError where
displayException = pretty
-- | Type class that provides the ability to convert
-- something from a Micheline Expression.
class FromExpression a where
fromExpression :: Expression -> Either FromExpressionError a
instance UnpackedValScope t => FromExpression (Value t) where
-- | `05` is the prefix for serialized Michelson value.
fromExpression =
first FromExpressionError . unpackValue' . ("\05" <>) . encodeExpression
instance FromExpression [ExpandedOp] where
fromExpression = first FromExpressionError . unpackInstr' . encodeExpression
instance FromExpression Untyped.Contract where
fromExpression =
first FromExpressionError . launchGet decodeContract .
LBS.fromStrict . encodeExpression