morley-1.2.0: src/Michelson/Interpret.hs
-- | Module, containing function to interpret Michelson
-- instructions against given context and input stack.
module Michelson.Interpret
( ContractEnv (..)
, InterpreterState (..)
, MichelsonFailed (..)
, RemainingSteps (..)
, SomeItStack (..)
, MorleyLogs (..)
, noMorleyLogs
, interpret
, interpretInstr
, ContractReturn
, mkInitStack
, fromFinalStack
, interpretUntyped
, InterpretError (..)
, InterpretResult (..)
, EvalM
, InstrRunner
, runInstr
, runInstrNoGas
, runUnpack
-- * Internals
, initInterpreterState
, handleContractReturn
, runInstrImpl
) where
import Prelude hiding (EQ, GT, LT)
import Control.Monad.Except (MonadError, throwError)
import Data.Default (Default(..))
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Singletons (Sing)
import Data.Vinyl (Rec(..), (<+>))
import Fmt (Buildable(build), Builder, genericF, pretty)
import Michelson.Interpret.Pack (packValue')
import Michelson.Interpret.Unpack (UnpackError, unpackValue')
import Michelson.TypeCheck
(SomeContract(..), TCError, TcOriginatedContracts, matchTypes, runTypeCheck, typeCheckContract,
typeCheckValue)
import Michelson.Typed
import qualified Michelson.Typed as T
import qualified Michelson.Untyped as U
import Tezos.Address (Address(..))
import Tezos.Core (ChainId, Mutez, Timestamp)
import Tezos.Crypto (KeyHash, blake2b, checkSignature, hashKey, sha256, sha512)
import Util.Peano (LongerThan, Peano, SingNat(SS, SZ))
import Util.TH
import Util.Type
import Util.Typeable
-- | Environment for contract execution.
data ContractEnv = ContractEnv
{ ceNow :: Timestamp
-- ^ Timestamp returned by the 'NOW' instruction.
, ceMaxSteps :: RemainingSteps
-- ^ Number of steps after which execution unconditionally terminates.
, ceBalance :: Mutez
-- ^ Current amount of mutez of the current contract.
, ceContracts :: TcOriginatedContracts
-- ^ Mapping from existing contracts' addresses to their executable
-- representation.
, ceSelf :: Address
-- ^ Address of the interpreted contract.
, ceSource :: Address
-- ^ The contract that initiated the current transaction.
, ceSender :: Address
-- ^ The contract that initiated the current internal transaction.
, ceAmount :: Mutez
-- ^ Amount of the current transaction.
, ceChainId :: ChainId
-- ^ Identifier of the current chain.
}
-- | Represents `[FAILED]` state of a Michelson program. Contains
-- value that was on top of the stack when `FAILWITH` was called.
data MichelsonFailed where
MichelsonFailedWith :: (Typeable t, SingI t) => T.Value t -> MichelsonFailed
MichelsonArithError
:: (Typeable n, Typeable m, Typeable instr)
=> ArithError (Value' instr n) (Value' instr m) -> MichelsonFailed
MichelsonGasExhaustion :: MichelsonFailed
MichelsonFailedTestAssert :: Text -> MichelsonFailed
MichelsonAmbigousEpRef :: EpName -> EpAddress -> MichelsonFailed
deriving stock instance Show MichelsonFailed
instance Eq MichelsonFailed where
MichelsonFailedWith v1 == MichelsonFailedWith v2 = v1 `eqParam1` v2
MichelsonFailedWith _ == _ = False
MichelsonArithError ae1 == MichelsonArithError ae2 = ae1 `eqParam2` ae2
MichelsonArithError _ == _ = False
MichelsonGasExhaustion == MichelsonGasExhaustion = True
MichelsonGasExhaustion == _ = False
MichelsonFailedTestAssert t1 == MichelsonFailedTestAssert t2 = t1 == t2
MichelsonFailedTestAssert _ == _ = False
MichelsonAmbigousEpRef ep1 epAddr1 == MichelsonAmbigousEpRef ep2 epAddr2 =
ep1 == ep2 && epAddr1 == epAddr2
MichelsonAmbigousEpRef _ _ == _ = False
instance Buildable MichelsonFailed where
build =
\case
MichelsonFailedWith (v :: T.Value t) ->
"Reached FAILWITH instruction with " <> formatValue v
MichelsonArithError v -> build v
MichelsonGasExhaustion ->
"Gas limit exceeded on contract execution"
MichelsonFailedTestAssert t -> build t
MichelsonAmbigousEpRef instrEp epAddr ->
"Ambigous entrypoint reference. `CONTRACT %" <> build instrEp <> "` \
\called over address " <> build epAddr
where
formatValue :: forall t . SingI t => Value t -> Builder
formatValue v =
case T.checkOpPresence (sing @t) of
OpPresent -> "<value with operations>"
OpAbsent -> build (untypeValue v)
data InterpretError
= RuntimeFailure (MichelsonFailed, MorleyLogs)
| IllTypedContract TCError
| IllTypedParam TCError
| IllTypedStorage TCError
deriving stock (Generic)
deriving stock instance Show InterpretError
instance Buildable InterpretError where
build = genericF
data InterpretResult where
InterpretResult
:: ( StorageScope st )
=> { iurOps :: [Operation]
, iurNewStorage :: T.Value st
, iurNewState :: InterpreterState
}
-> InterpretResult
deriving stock instance Show InterpretResult
constructIR ::
(StorageScope st) =>
(([Operation], Value' Instr st), InterpreterState) ->
InterpretResult
constructIR ((ops, val), st) =
InterpretResult
{ iurOps = ops
, iurNewStorage = val
, iurNewState = st
}
-- | Morley logs for interpreter state.
newtype MorleyLogs = MorleyLogs
{ unMorleyLogs :: [Text]
-- ^ Logs in reverse order.
} deriving stock (Eq, Show, Generic)
deriving newtype (Default, Buildable)
instance NFData MorleyLogs
noMorleyLogs :: MorleyLogs
noMorleyLogs = MorleyLogs []
-- | Interpret a contract without performing any side effects.
-- This function uses untyped representation of contract, parameter and storage.
-- Mostly used for testing.
interpretUntyped
:: U.Contract
-> U.Value
-> U.Value
-> ContractEnv
-> Either InterpretError InterpretResult
interpretUntyped uContract@U.Contract{..} paramU initStU env = do
SomeContract (FullContract (instr :: ContractCode cp st) _ _)
<- first IllTypedContract $ typeCheckContract (ceContracts env) uContract
-- Do creates dummy scope to somehow overcome this:
-- GHC internal error: ‘st’ is not in scope during type checking, but it passed the renamer.
do
let
runTC :: forall t. SingI t => U.Value -> Either TCError (Value t)
runTC =
runTypeCheck contractParameter (ceContracts env) .
usingReaderT def .
typeCheckValue @t
paramV <- first IllTypedParam $ runTC @cp paramU
initStV <- first IllTypedStorage $ runTC @st initStU
handleContractReturn $
interpret instr epcCallRootUnsafe paramV initStV env
type ContractReturn st =
(Either MichelsonFailed ([Operation], T.Value st), InterpreterState)
handleContractReturn
:: (StorageScope st)
=> ContractReturn st -> Either InterpretError InterpretResult
handleContractReturn (ei, s) =
bimap (RuntimeFailure . (, isMorleyLogs s)) (constructIR . (, s)) ei
interpret'
:: forall cp st arg.
ContractCode cp st
-> EntryPointCallT cp arg
-> T.Value arg
-> T.Value st
-> ContractEnv
-> InterpreterState
-> ContractReturn st
interpret' instr epc param initSt env ist = first (fmap fromFinalStack) $
runEvalOp
(runInstr instr $ mkInitStack (liftCallArg epc param) initSt)
env
ist
mkInitStack :: T.Value param -> T.Value st -> Rec T.Value (ContractInp param st)
mkInitStack param st = T.VPair (param, st) :& RNil
fromFinalStack :: Rec T.Value (ContractOut st) -> ([T.Operation], T.Value st)
fromFinalStack (T.VPair (T.VList ops, st) :& RNil) =
(map (\(T.VOp op) -> op) ops, st)
interpret
:: ContractCode cp st
-> EntryPointCallT cp arg
-> T.Value arg
-> T.Value st
-> ContractEnv
-> ContractReturn st
interpret instr epc param initSt env =
interpret' instr epc param initSt env (initInterpreterState env)
initInterpreterState :: ContractEnv -> InterpreterState
initInterpreterState env = InterpreterState def (ceMaxSteps env)
-- | Interpret an instruction in vacuum, putting no extra contraints on
-- its execution.
--
-- Mostly for testing purposes.
interpretInstr
:: ContractEnv
-> Instr inp out
-> Rec T.Value inp
-> Either MichelsonFailed (Rec T.Value out)
interpretInstr env instr inpSt =
fst $
runEvalOp
(runInstr instr inpSt)
env
InterpreterState{ isMorleyLogs = MorleyLogs [], isRemainingSteps = 9999999999 }
data SomeItStack where
SomeItStack :: T.ExtInstr inp -> Rec T.Value inp -> SomeItStack
newtype RemainingSteps = RemainingSteps Word64
deriving stock (Show, Generic)
deriving newtype (Eq, Ord, Buildable, Num)
instance NFData RemainingSteps
data InterpreterState = InterpreterState
{ isMorleyLogs :: MorleyLogs
, isRemainingSteps :: RemainingSteps
} deriving stock (Show, Generic)
instance NFData InterpreterState
type EvalOp a =
ExceptT MichelsonFailed
(ReaderT ContractEnv
(State InterpreterState)) a
runEvalOp
:: EvalOp a
-> ContractEnv
-> InterpreterState
-> (Either MichelsonFailed a, InterpreterState)
runEvalOp act env initSt =
flip runState initSt $ usingReaderT env $ runExceptT act
type EvalM m =
( MonadReader ContractEnv m
, MonadState InterpreterState m
, MonadError MichelsonFailed m
)
type InstrRunner m =
forall inp out.
Instr inp out
-> Rec (T.Value) inp
-> m (Rec (T.Value) out)
-- | Function to change amount of remaining steps stored in State monad
runInstr :: EvalM m => InstrRunner m
runInstr i@(Seq _i1 _i2) r = runInstrImpl runInstr i r
runInstr i@(InstrWithNotes _ _i1) r = runInstrImpl runInstr i r
runInstr i@(InstrWithVarNotes _ _i1) r = runInstrImpl runInstr i r
runInstr i@Nop r = runInstrImpl runInstr i r
runInstr i@(Nested _) r = runInstrImpl runInstr i r
runInstr i r = do
rs <- gets isRemainingSteps
if rs == 0
then throwError $ MichelsonGasExhaustion
else do
modify (\s -> s {isRemainingSteps = rs - 1})
runInstrImpl runInstr i r
runInstrNoGas :: EvalM m => InstrRunner m
runInstrNoGas = runInstrImpl runInstrNoGas
-- | Function to interpret Michelson instruction(s) against given stack.
runInstrImpl :: EvalM m => InstrRunner m -> InstrRunner m
runInstrImpl runner (Seq i1 i2) r = runner i1 r >>= \r' -> runner i2 r'
runInstrImpl runner (InstrWithNotes _ i) r = runner i r
runInstrImpl runner (InstrWithVarNotes _ i) r = runner i r
runInstrImpl runner (FrameInstr (_ :: Proxy s) i) r = do
let (inp, end) = rsplit @_ @_ @s r
out <- runInstrImpl runner i inp
return (out <+> end)
runInstrImpl _ Nop r = pure $ r
runInstrImpl _ (Ext nop) r = r <$ interpretExt (SomeItStack nop r)
runInstrImpl runner (Nested sq) r = runInstrImpl runner sq r
runInstrImpl runner (DocGroup _ sq) r = runInstrImpl runner sq r
runInstrImpl _ DROP (_ :& r) = pure $ r
runInstrImpl runner (DROPN s) stack =
case s of
SZ -> pure stack
SS s' -> case stack of
-- Note: we intentionally do not use `runner` to recursively
-- interpret `DROPN` here.
-- All these recursive calls together correspond to a single
-- Michelson instruction call.
-- This recursion is implementation detail of `DROPN`.
-- The same reasoning applies to other instructions parameterized
-- by a natural number like 'DIPN'.
(_ :& r) -> runInstrImpl runner (DROPN s') r
runInstrImpl _ DUP (a :& r) = pure $ a :& a :& r
runInstrImpl _ SWAP (a :& b :& r) = pure $ b :& a :& r
runInstrImpl _ (DIG nSing0) input0 =
pure $ go (nSing0, input0)
where
go :: forall (n :: Peano) inp out a. T.ConstraintDIG n inp out a =>
(Sing n, Rec T.Value inp) -> Rec T.Value out
go = \case
(SZ, stack) -> stack
(SS nSing, b :& r) -> case go (nSing, r) of
(a :& resTail) -> a :& b :& resTail
runInstrImpl _ (DUG nSing0) input0 =
pure $ go (nSing0, input0)
where
go :: forall (n :: Peano) inp out a. T.ConstraintDUG n inp out a =>
(Sing n, Rec T.Value inp) -> Rec T.Value out
go = \case
(SZ, stack) -> stack
(SS s', a :& b :& r) -> b :& go (s', a :& r)
runInstrImpl _ (PUSH v) r = pure $ v :& r
runInstrImpl _ SOME (a :& r) = pure $ VOption (Just a) :& r
runInstrImpl _ NONE r = pure $ VOption Nothing :& r
runInstrImpl _ UNIT r = pure $ VUnit :& r
runInstrImpl runner (IF_NONE _bNone bJust) (VOption (Just a) :& r) = runner bJust (a :& r)
runInstrImpl runner (IF_NONE bNone _bJust) (VOption Nothing :& r) = runner bNone r
runInstrImpl _ PAIR (a :& b :& r) = pure $ VPair (a, b) :& r
runInstrImpl _ (AnnCAR _) (VPair (a, _b) :& r) = pure $ a :& r
runInstrImpl _ (AnnCDR _) (VPair (_a, b) :& r) = pure $ b :& r
runInstrImpl _ LEFT (a :& r) = pure $ (VOr $ Left a) :& r
runInstrImpl _ RIGHT (b :& r) = pure $ (VOr $ Right b) :& r
runInstrImpl runner (IF_LEFT bLeft _) (VOr (Left a) :& r) = runner bLeft (a :& r)
runInstrImpl runner (IF_LEFT _ bRight) (VOr (Right a) :& r) = runner bRight (a :& r)
-- More here
runInstrImpl _ NIL r = pure $ VList [] :& r
runInstrImpl _ CONS (a :& VList l :& r) = pure $ VList (a : l) :& r
runInstrImpl runner (IF_CONS _ bNil) (VList [] :& r) = runner bNil r
runInstrImpl runner (IF_CONS bCons _) (VList (lh : lr) :& r) = runner bCons (lh :& VList lr :& r)
runInstrImpl _ SIZE (a :& r) = pure $ (VNat $ (fromInteger . toInteger) $ evalSize a) :& r
runInstrImpl _ EMPTY_SET r = pure $ VSet Set.empty :& r
runInstrImpl _ EMPTY_MAP r = pure $ VMap Map.empty :& r
runInstrImpl _ EMPTY_BIG_MAP r = pure $ VBigMap Map.empty :& r
runInstrImpl runner (MAP ops) (a :& r) =
case ops of
(code :: Instr (MapOpInp c ': s) (b ': s)) -> do
-- Evaluation must preserve all stack modifications that @MAP@'s does.
(newStack, newList) <- foldlM (\(curStack, curList) (val :: T.Value (MapOpInp c)) -> do
res <- runner code (val :& curStack)
case res of
((nextVal :: T.Value b) :& nextStack) -> pure (nextStack, nextVal : curList))
(r, []) (mapOpToList @c a)
pure $ mapOpFromList a (reverse newList) :& newStack
runInstrImpl runner (ITER ops) (a :& r) =
case ops of
(code :: Instr (IterOpEl c ': s) s) ->
case iterOpDetachOne @c a of
(Just x, xs) -> do
res <- runner code (x :& r)
runner (ITER code) (xs :& res)
(Nothing, _) -> pure r
runInstrImpl _ MEM (a :& b :& r) = pure $ (VBool (evalMem a b)) :& r
runInstrImpl _ GET (a :& b :& r) = pure $ VOption (evalGet a b) :& r
runInstrImpl _ UPDATE (a :& b :& c :& r) = pure $ evalUpd a b c :& r
runInstrImpl runner (IF bTrue _) (VBool True :& r) = runner bTrue r
runInstrImpl runner (IF _ bFalse) (VBool False :& r) = runner bFalse r
runInstrImpl _ (LOOP _) (VBool False :& r) = pure $ r
runInstrImpl runner (LOOP ops) (VBool True :& r) = do
res <- runner ops r
runner (LOOP ops) res
runInstrImpl _ (LOOP_LEFT _) (VOr (Right a) :&r) = pure $ a :& r
runInstrImpl runner (LOOP_LEFT ops) (VOr (Left a) :& r) = do
res <- runner ops (a :& r)
runner (LOOP_LEFT ops) res
runInstrImpl _ (LAMBDA lam) r = pure $ lam :& r
runInstrImpl runner EXEC (a :& VLam (T.rfAnyInstr -> lBody) :& r) = do
res <- runner lBody (a :& RNil)
pure $ res <+> r
runInstrImpl _ APPLY ((a :: T.Value a) :& VLam lBody :& r) = do
pure $ VLam (T.rfMapAnyInstr doApply lBody) :& r
where
doApply :: Instr ('TPair a i ': s) o -> Instr (i ': s) o
doApply b = PUSH a `Seq` PAIR `Seq` Nested b
runInstrImpl runner (DIP i) (a :& r) = do
res <- runner i r
pure $ a :& res
runInstrImpl runner (DIPN s i) stack =
case s of
SZ -> runner i stack
SS s' -> case stack of
(a :& r) -> (a :&) <$> runInstrImpl runner (DIPN s' i) r
runInstrImpl _ FAILWITH (a :& _) = throwError $ MichelsonFailedWith a
runInstrImpl _ CAST (a :& r) = pure $ a :& r
runInstrImpl _ RENAME (a :& r) = pure $ a :& r
runInstrImpl _ PACK (a :& r) = pure $ (VBytes $ packValue' a) :& r
runInstrImpl _ UNPACK (VBytes a :& r) =
pure $ (VOption . rightToMaybe $ runUnpack a) :& r
runInstrImpl _ CONCAT (a :& b :& r) = pure $ evalConcat a b :& r
runInstrImpl _ CONCAT' (VList a :& r) = pure $ evalConcat' a :& r
runInstrImpl _ SLICE (VNat o :& VNat l :& s :& r) =
pure $ VOption (evalSlice o l s) :& r
runInstrImpl _ ISNAT (VInt i :& r) =
if i < 0
then pure $ VOption Nothing :& r
else pure $ VOption (Just (VNat $ fromInteger i)) :& r
runInstrImpl _ ADD (l :& r :& rest) =
(:& rest) <$> runArithOp (Proxy @Add) l r
runInstrImpl _ SUB (l :& r :& rest) = (:& rest) <$> runArithOp (Proxy @Sub) l r
runInstrImpl _ MUL (l :& r :& rest) = (:& rest) <$> runArithOp (Proxy @Mul) l r
runInstrImpl _ EDIV (l :& r :& rest) = pure $ evalEDivOp l r :& rest
runInstrImpl _ ABS (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Abs) a) :& rest
runInstrImpl _ NEG (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Neg) a) :& rest
runInstrImpl _ LSL (x :& s :& rest) = (:& rest) <$> runArithOp (Proxy @Lsl) x s
runInstrImpl _ LSR (x :& s :& rest) = (:& rest) <$> runArithOp (Proxy @Lsr) x s
runInstrImpl _ OR (l :& r :& rest) = (:& rest) <$> runArithOp (Proxy @Or) l r
runInstrImpl _ AND (l :& r :& rest) = (:& rest) <$> runArithOp (Proxy @And) l r
runInstrImpl _ XOR (l :& r :& rest) = (:& rest) <$> runArithOp (Proxy @Xor) l r
runInstrImpl _ NOT (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Not) a) :& rest
runInstrImpl _ COMPARE (l :& r :& rest) = pure $ (T.VInt (compareOp l r)) :& rest
runInstrImpl _ EQ (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Eq') a) :& rest
runInstrImpl _ NEQ (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Neq) a) :& rest
runInstrImpl _ LT (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Lt) a) :& rest
runInstrImpl _ GT (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Gt) a) :& rest
runInstrImpl _ LE (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Le) a) :& rest
runInstrImpl _ GE (a :& rest) = pure $ (evalUnaryArithOp (Proxy @Ge) a) :& rest
runInstrImpl _ INT (VNat n :& r) = pure $ (VInt $ toInteger n) :& r
runInstrImpl _ (SELF sepc :: Instr inp out) r = do
ContractEnv{..} <- ask
case Proxy @out of
(_ :: Proxy ('TContract cp ': s)) -> do
pure $ VContract ceSelf sepc :& r
runInstrImpl _ (CONTRACT (nt :: T.Notes a) instrEpName) (VAddress epAddr :& r) = do
ContractEnv{..} <- ask
let T.EpAddress addr addrEpName = epAddr
epName <- case (instrEpName, addrEpName) of
(DefEpName, DefEpName) -> pure DefEpName
(DefEpName, en) -> pure en
(en, DefEpName) -> pure en
_ -> throwError $ MichelsonAmbigousEpRef instrEpName epAddr
pure $ case addr of
KeyAddress _ ->
castContract addr epName T.tyImplicitAccountParam :& r
ContractAddress ca ->
case Map.lookup ca ceContracts of
-- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has
-- valid parameter type. Should be not necessary after [#36].
Just tc@(U.ParameterType (AsUTypeExt (_ :: Sing tc) tcNotes) rootAnn) ->
let paramNotes = ParamNotesUnsafe tcNotes rootAnn in
case T.checkScope @(T.ParameterScope tc) of
Right Dict -> castContract addr epName paramNotes :& r
_ -> error $ "Illegal type in parameter of env contract: " <> pretty tc
-- TODO [#36]: we can do this safely once 'TcOriginatedContracts' stores
-- typed stuff.
Nothing -> VOption Nothing :& r
where
castContract
:: forall p. T.ParameterScope p
=> Address -> EpName -> T.ParamNotes p -> T.Value ('TOption ('TContract a))
castContract addr epName param = VOption $ do
-- As we are within Maybe monad, pattern-match failure results in Nothing
MkEntryPointCallRes na epc <- T.mkEntryPointCall epName param
Right (Refl, _) <- pure $ matchTypes nt na
return $ VContract addr (T.SomeEpc epc)
runInstrImpl _ TRANSFER_TOKENS (p :& VMutez mutez :& contract :& r) =
pure $ VOp (OpTransferTokens $ TransferTokens p mutez contract) :& r
runInstrImpl _ SET_DELEGATE (VOption mbKeyHash :& r) =
case mbKeyHash of
Just (VKeyHash k) -> pure $ VOp (OpSetDelegate $ SetDelegate $ Just k) :& r
Nothing -> pure $ VOp (OpSetDelegate $ SetDelegate $ Nothing) :& r
runInstrImpl _ (CREATE_CONTRACT fullContract)
(VOption mbKeyHash :& VMutez m :& g :& r) = do
originator <- ceSelf <$> ask
let ops = fcCode fullContract
let resAddr = U.mkContractAddress $ createOrigOp originator mbKeyHash m ops g
let resEpAddr = EpAddress resAddr def
let resOp = CreateContract originator (unwrapMbKeyHash mbKeyHash) m g ops
pure $ VOp (OpCreateContract resOp)
:& (VAddress resEpAddr)
:& r
runInstrImpl _ IMPLICIT_ACCOUNT (VKeyHash k :& r) =
pure $ VContract (KeyAddress k) sepcPrimitive :& r
runInstrImpl _ NOW r = do
ContractEnv{..} <- ask
pure $ (VTimestamp ceNow) :& r
runInstrImpl _ AMOUNT r = do
ContractEnv{..} <- ask
pure $ (VMutez ceAmount) :& r
runInstrImpl _ BALANCE r = do
ContractEnv{..} <- ask
pure $ (VMutez ceBalance) :& r
runInstrImpl _ CHECK_SIGNATURE (VKey k :& VSignature v :&
VBytes b :& r) = pure $ (VBool $ checkSignature k v b) :& r
runInstrImpl _ SHA256 (VBytes b :& r) = pure $ (VBytes $ sha256 b) :& r
runInstrImpl _ SHA512 (VBytes b :& r) = pure $ (VBytes $ sha512 b) :& r
runInstrImpl _ BLAKE2B (VBytes b :& r) = pure $ (VBytes $ blake2b b) :& r
runInstrImpl _ HASH_KEY (VKey k :& r) = pure $ (VKeyHash $ hashKey k) :& r
runInstrImpl _ SOURCE r = do
ContractEnv{..} <- ask
pure $ (VAddress $ EpAddress ceSource def) :& r
runInstrImpl _ SENDER r = do
ContractEnv{..} <- ask
pure $ (VAddress $ EpAddress ceSender def) :& r
runInstrImpl _ ADDRESS (VContract a sepc :& r) =
pure $ (VAddress $ EpAddress a (sepcName sepc)) :& r
runInstrImpl _ CHAIN_ID r = do
ContractEnv{..} <- ask
pure $ VChainId ceChainId :& r
-- | Evaluates an arithmetic operation and either fails or proceeds.
runArithOp
:: (ArithOp aop n m, Typeable n, Typeable m, EvalM monad)
=> proxy aop
-> Value n
-> Value m
-> monad (Value (ArithRes aop n m))
runArithOp op l r = case evalOp op l r of
Left err -> throwError (MichelsonArithError err)
Right res -> pure res
-- | Unpacks given raw data into a typed value.
runUnpack
:: forall t. (UnpackedValScope t)
=> ByteString
-> Either UnpackError (T.Value t)
runUnpack bs =
-- TODO [TM-80] Gas consumption here should depend on unpacked data size
-- and size of resulting expression, errors would also spend some (all equally).
-- Fortunatelly, the inner decoding logic does not need to know anything about gas use.
unpackValue' bs
createOrigOp
:: (SingI param, StorageScope store)
=> Address
-> Maybe (T.Value 'T.TKeyHash)
-> Mutez
-> ContractCode param store
-> Value' Instr store
-> U.OriginationOperation
createOrigOp originator mbDelegate bal contract g =
U.OriginationOperation
{ ooOriginator = originator
, ooDelegate = unwrapMbKeyHash mbDelegate
, ooBalance = bal
, ooStorage = untypeValue g
, ooContract = convertContractCode contract
}
unwrapMbKeyHash :: Maybe (T.Value 'T.TKeyHash) -> Maybe KeyHash
unwrapMbKeyHash mbKeyHash = mbKeyHash <&> \(VKeyHash keyHash) -> keyHash
interpretExt :: EvalM m => SomeItStack -> m ()
interpretExt (SomeItStack (T.PRINT (T.PrintComment pc)) st) = do
let getEl (Left l) = l
getEl (Right str) = withStackElem str st show
modify (\s -> s {isMorleyLogs = MorleyLogs $ mconcat (map getEl pc) : unMorleyLogs (isMorleyLogs s)})
interpretExt (SomeItStack (T.TEST_ASSERT (T.TestAssert nm pc instr)) st) = do
ost <- runInstrNoGas instr st
let ((T.fromVal -> succeeded) :& _) = ost
unless succeeded $ do
interpretExt (SomeItStack (T.PRINT pc) st)
throwError $ MichelsonFailedTestAssert $ "TEST_ASSERT " <> nm <> " failed"
interpretExt (SomeItStack T.DOC_ITEM{} _) = pass
interpretExt (SomeItStack T.COMMENT_ITEM{} _) = pass
-- | Access given stack reference (in CPS style).
withStackElem
:: forall st a.
T.StackRef st
-> Rec T.Value st
-> (forall t. T.Value t -> a)
-> a
withStackElem (T.StackRef sn) vals cont =
loop (vals, sn)
where
loop
:: forall s (n :: Peano). (LongerThan s n)
=> (Rec T.Value s, Sing n) -> a
loop = \case
(e :& _, SZ) -> cont e
(_ :& es, SS n) -> loop (es, n)
$(deriveGADTNFData ''MichelsonFailed)