-- SPDX-FileCopyrightText: 2020 Tocqueville Group -- -- SPDX-License-Identifier: LicenseRef-MIT-TQ module Lorentz.Run ( CompilationOptions(..) , defaultCompilationOptions , compileLorentz , compileLorentzWithOptions , Contract(..) , defaultContract , compileLorentzContract , interpretLorentzInstr , interpretLorentzLambda , analyzeLorentz ) where import Data.Constraint ((\\)) import Data.Default (def) import Data.Vinyl.Core (Rec(..)) import Lorentz.Annotation import Lorentz.Base import Lorentz.Constraints import Lorentz.Entrypoints import Michelson.Analyzer (AnalyzerRes, analyze) import Michelson.Interpret import Michelson.Optimizer (OptimizerConf, optimizeWithConf) import Michelson.Text (MText) import Michelson.Typed (Instr(..), IsoValue, IsoValuesStack(..), ToT, ToTs, starParamNotes) import qualified Michelson.Typed as M (Contract(..)) import qualified Michelson.Untyped as U (canonicalEntriesOrder) -- | Options to control Lorentz to Michelson compilation. data CompilationOptions = CompilationOptions { coOptimizerConf :: Maybe OptimizerConf -- ^ Config for Michelson optimizer. , coStringTransformer :: (Bool, MText -> MText) -- ^ Function to transform strings with. See 'transformStringsLorentz'. , coBytesTransformer :: (Bool, ByteString -> ByteString) -- ^ Function to transform byte strings with. See 'transformBytesLorentz'. } -- | Runs Michelson optimizer with default config and does not touch strings and bytes. defaultCompilationOptions :: CompilationOptions defaultCompilationOptions = CompilationOptions { coOptimizerConf = Just def , coStringTransformer = (False, id) , coBytesTransformer = (False, id) } -- | For use outside of Lorentz. Will use 'defaultCompilationOptions'. compileLorentz :: (inp :-> out) -> Instr (ToTs inp) (ToTs out) compileLorentz = compileLorentzWithOptions defaultCompilationOptions -- | Compile Lorentz code, optionally running the optimizer, string and byte transformers. compileLorentzWithOptions :: CompilationOptions -> (inp :-> out) -> Instr (ToTs inp) (ToTs out) compileLorentzWithOptions CompilationOptions{..} = maybe id optimizeWithConf coOptimizerConf . iAnyCode . uncurry transformStringsLorentz coStringTransformer . uncurry transformBytesLorentz coBytesTransformer -- | Code for a contract along with compilation options for the Lorentz compiler. -- -- It is expected that a 'Contract' is one packaged entity, wholly controlled by its author. -- Therefore the author should be able to set all options that control contract's behavior. -- -- This helps ensure that a given contract will be interpreted in the same way in all -- environments, like production and testing. -- -- Raw 'ContractCode' should not be used for distribution of contracts. data Contract cp st = Contract { cCode :: ContractCode cp st -- ^ The contract itself. , cDisableInitialCast :: Bool -- ^ Flag which defines whether compiled Michelson contract -- will have @CAST@ (which drops parameter annotations) -- as a first instruction. Note that when -- flag is false, there still may be no @CAST@ (in case -- when parameter type has no annotations). , cCompilationOptions :: CompilationOptions -- ^ General compilation options for the Lorentz compiler. } -- | Compile contract with 'defaultCompilationOptions' and 'cDisableInitialCast' set to @False@. defaultContract :: ContractCode cp st -> Contract cp st defaultContract code = Contract { cCode = code , cDisableInitialCast = False , cCompilationOptions = defaultCompilationOptions } -- | Compile a whole contract to Michelson. -- -- Note that compiled contract can be ill-typed in terms of Michelson code -- when some of the compilation options are used (e.g. when 'ccoDisableInitialCast' -- is @True@, resulted contract can be ill-typed). -- However, compilation with 'defaultContractCompilationOptions' should be valid. compileLorentzContract :: forall cp st. (NiceParameterFull cp, NiceStorage st) => Contract cp st -> M.Contract (ToT cp) (ToT st) compileLorentzContract Contract{..} = M.Contract { cCode = if (cpNotes == starParamNotes || cDisableInitialCast) then -- If contract parameter type has no annotations or explicitly asked, we drop CAST. compileLorentzWithOptions cCompilationOptions cCode else -- Perform CAST otherwise. compileLorentzWithOptions cCompilationOptions (I CAST # cCode :: ContractCode cp st) , cParamNotes = cpNotes , cStoreNotes = getAnnotation @st NotFollowEntrypoint , cEntriesOrder = U.canonicalEntriesOrder } \\ niceParameterEvi @cp \\ niceStorageEvi @st where cpNotes = parameterEntrypointsToNotes @cp -- | Interpret a Lorentz instruction, for test purposes. Note that this does not run the -- optimizer. interpretLorentzInstr :: (IsoValuesStack inp, IsoValuesStack out) => ContractEnv -> inp :-> out -> Rec Identity inp -> Either MichelsonFailed (Rec Identity out) interpretLorentzInstr env (compileLorentz -> instr) inp = fromValStack <$> interpretInstr env instr (toValStack inp) -- | Like 'interpretLorentzInstr', but works on lambda rather than -- arbitrary instruction. interpretLorentzLambda :: (IsoValue inp, IsoValue out) => ContractEnv -> Lambda inp out -> inp -> Either MichelsonFailed out interpretLorentzLambda env instr inp = do res <- interpretLorentzInstr env instr (Identity inp :& RNil) let Identity out :& RNil = res return out -- | Lorentz version of analyzer. analyzeLorentz :: inp :-> out -> AnalyzerRes analyzeLorentz = analyze . compileLorentz