morley 1.0.0 → 1.1.0
raw patch · 98 files changed
+2670/−1417 lines, 98 filesdep +gaugedep +tezos-bake-monitor-libdep ~morley-prelude
Dependencies added: gauge, tezos-bake-monitor-lib
Dependency ranges changed: morley-prelude
Files
- CHANGES.md +44/−1
- CONTRIBUTING.md +0/−84
- README.md +11/−30
- app/Main.hs +31/−190
- bench/Main.hs +70/−0
- morley.cabal +33/−7
- src/Michelson/Doc.hs +40/−8
- src/Michelson/Doc/Test.hs +33/−6
- src/Michelson/ErrorPos.hs +10/−2
- src/Michelson/Interpret.hs +85/−74
- src/Michelson/Interpret/Pack.hs +9/−14
- src/Michelson/Interpret/Unpack.hs +1/−1
- src/Michelson/Macro.hs +10/−0
- src/Michelson/OpSize.hs +16/−14
- src/Michelson/Optimizer.hs +64/−0
- src/Michelson/Parser.hs +1/−0
- src/Michelson/Parser/Error.hs +6/−2
- src/Michelson/Parser/Instr.hs +3/−2
- src/Michelson/Parser/Macro.hs +10/−1
- src/Michelson/Parser/Types.hs +1/−1
- src/Michelson/Printer.hs +4/−4
- src/Michelson/Printer/Util.hs +5/−0
- src/Michelson/Runtime.hs +8/−9
- src/Michelson/Test.hs +2/−0
- src/Michelson/Test/Import.hs +9/−9
- src/Michelson/Test/Unit.hs +25/−6
- src/Michelson/Text.hs +11/−4
- src/Michelson/TypeCheck.hs +0/−1
- src/Michelson/TypeCheck/Error.hs +15/−5
- src/Michelson/TypeCheck/Ext.hs +6/−8
- src/Michelson/TypeCheck/Helpers.hs +25/−26
- src/Michelson/TypeCheck/Instr.hs +233/−252
- src/Michelson/TypeCheck/Types.hs +34/−35
- src/Michelson/TypeCheck/Value.hs +115/−128
- src/Michelson/Typed/Annotation.hs +66/−2
- src/Michelson/Typed/Arith.hs +9/−5
- src/Michelson/Typed/CValue.hs +5/−1
- src/Michelson/Typed/Convert.hs +30/−30
- src/Michelson/Typed/Doc.hs +25/−3
- src/Michelson/Typed/EntryPoints.hs +53/−18
- src/Michelson/Typed/Extract.hs +59/−52
- src/Michelson/Typed/Haskell/Doc.hs +6/−5
- src/Michelson/Typed/Haskell/Instr/Product.hs +1/−1
- src/Michelson/Typed/Haskell/Instr/Sum.hs +3/−3
- src/Michelson/Typed/Haskell/Value.hs +7/−3
- src/Michelson/Typed/Instr.hs +53/−23
- src/Michelson/Typed/T.hs +3/−1
- src/Michelson/Typed/Value.hs +27/−2
- src/Michelson/Untyped/Annotation.hs +127/−19
- src/Michelson/Untyped/Contract.hs +2/−0
- src/Michelson/Untyped/EntryPoints.hs +43/−3
- src/Michelson/Untyped/Ext.hs +16/−0
- src/Michelson/Untyped/Instr.hs +36/−28
- src/Michelson/Untyped/Type.hs +53/−48
- src/Michelson/Untyped/Value.hs +7/−1
- src/Morley/CLI.hs +145/−0
- src/Morley/Micheline.hs +64/−0
- src/Tezos/Address.hs +21/−4
- src/Tezos/Core.hs +13/−1
- src/Tezos/Crypto.hs +20/−5
- src/Tezos/Crypto/Ed25519.hs +9/−3
- src/Tezos/Crypto/P256.hs +9/−3
- src/Tezos/Crypto/Secp256k1.hs +23/−4
- src/Tezos/Crypto/Util.hs +3/−0
- src/Util/CLI.hs +166/−0
- src/Util/Exception.hs +49/−1
- src/Util/Label.hs +44/−0
- src/Util/Markdown.hs +25/−4
- src/Util/Named.hs +18/−1
- src/Util/Peano.hs +33/−0
- src/Util/Positive.hs +3/−1
- src/Util/TH.hs +40/−0
- src/Util/Text.hs +4/−0
- src/Util/Type.hs +18/−4
- src/Util/TypeLits.hs +1/−0
- src/Util/Typeable.hs +13/−0
- test/Test/Analyzer.hs +1/−1
- test/Test/Ext.hs +8/−6
- test/Test/Interpreter.hs +67/−142
- test/Test/Interpreter/Apply.hs +4/−2
- test/Test/Interpreter/Auction.hs +3/−1
- test/Test/Interpreter/Balance.hs +107/−0
- test/Test/Interpreter/CallSelf.hs +4/−2
- test/Test/Interpreter/Compare.hs +3/−1
- test/Test/Interpreter/ComparePairs.hs +3/−1
- test/Test/Interpreter/Conditionals.hs +3/−1
- test/Test/Interpreter/ContractOp.hs +5/−3
- test/Test/Interpreter/EnvironmentSpec.hs +10/−5
- test/Test/Interpreter/StackRef.hs +10/−3
- test/Test/Interpreter/StringCaller.hs +11/−8
- test/Test/Michelson/Runtime.hs +4/−1
- test/Test/OpSize.hs +7/−1
- test/Test/Optimizer.hs +33/−2
- test/Test/Preprocess.hs +2/−2
- test/Test/Printer/Michelson.hs +10/−4
- test/Test/Serialization/Michelson.hs +6/−1
- test/Test/Typecheck.hs +15/−15
- test/Test/Util/Contracts.hs +32/−12
CHANGES.md view
@@ -2,11 +2,54 @@ ========== <!-- Append new entries here --> +1.1.0+=====+* [!306](https://gitlab.com/morley-framework/morley/-/merge_requests/306)+ Added PAIR/UNPAIR rule to optimizer.+* [!314](https://gitlab.com/morley-framework/morley/-/merge_requests/314)+ Fixed a bug in the implementation of `MAP` operation: it did not preserve modifications to+ stack.+* [!261](https://gitlab.com/morley-framework/morley/merge_requests/261)+ Slightly modified interpreter API.+* [!313](https://gitlab.com/morley-framework/morley/-/merge_requests/313)+ Made `typeCheckValue` polymorphic in desired type, instead of using+ existential wrappers.+* [!310](https://gitlab.com/morley-framework/morley/merge_requests/232)+ + Add `DGeneralInfoSection` documentation section with git revision and+ potentially other info.+ + Add `buildLorentzDocWithGitRev` and `buildInstrDocWithGitRev` to+ API to add a git revision to contract docs from the executable.+* [!121](https://gitlab.com/morley-framework/morley/merge_requests/302)+ `BALANCE` instruction now returns the balance with funds from incoming+ transaction.+* [!294](https://gitlab.com/morley-framework/morley/-/merge_requests/294)+ + Added `Paths_*` modules to `autogen-modules` in cabal files. Removed `-O0`+ + from default GHC options. Please set `ghc-options` in your `stack.yaml` or+ `cabal.project.local`.+* [!271](https://gitlab.com/morley-framework/morley/merge_requests/271) Renamed+ 'Contract' to 'ContractCode', and appended "Code" to the names of two functions:+ 'convertContract' and 'printTypedContract'+* [!278](https://gitlab.com/morley-framework/morley/merge_requests/278)+ Added some utilities for command line option parsing, see `Util.CLI` and `Morley.CLI` modules.+* [!268](https://gitlab.com/morley-framework/morley/merge_requests/268)+ Test functions which import typed contract now return `FullContract` instead+ of `Contract`, thus preserving parameter and storage annotations. In case you+ don't need this behaviour, use `fcCode` for conversion.+ Test functions which import Lorentz contracts have been removed because they+ cannot be implemented sanely, and Lorentz is assumed to be used to generate+ code, do not use it for work with textual Michelson contracts.+* [!212](https://gitlab.com/morley-framework/morley/merge_requests/212)+ + Fix `AND` instruction return type.+ + Add `DUP n` macro support.+ + Fix `LAMBDA` instruction printer.+* [!265](https://gitlab.com/morley-framework/morley/merge_requests/265)+ The semicolons between instructions are now optional.+ 1.0.0 ===== * [!215](https://gitlab.com/morley-framework/morley/merge_requests/215)- Major change: all Lorentz functionality was moved into `morley-lorentz` package.+ Major change: all Lorentz functionality was moved into `lorentz` package. A small portion of testing library code was moved around (from `Lorentz.*` to `Michelson.*` or vice versa). 0.7.0
− CONTRIBUTING.md
@@ -1,84 +0,0 @@-# Contribution Guidelines--## Reporting Issues--Please [open an issue](https://gitlab.com/morley-framework/morley/issues/new)-if you find a bug or have a feature request.-Before submitting a bug report or feature request, check to make sure it hasn't already been submitted.--The more detailed your report is, the faster it can be resolved.-Please use issue templates to create an issue.--## Code--If you would like to contribute code to fix a bug, add a new feature, or-otherwise improve our project, merge requests are most welcome.--Our merge request template contains a [checklist](/.gitlab/merge_request_templates/default.md#white_check_mark-checklist-for-your-merge-request) of acceptance criteria for your merge request.-Please read it before you start contributing and make sure your contributions adhere to this checklist.--### Prelude--All Haskell code uses-[Universum](https://hackage.haskell.org/package/universum) as a-replacement for the default prelude.--### Tests--We use [`tasty`](https://hackage.haskell.org/package/tasty) as our primary top-level testing framework.-Some old code may use `hspec` instead, but all new code must use `tasty`.-We use [`tasty-discover`](https://hackage.haskell.org/package/tasty-discover) to automatically find all tests.-We still require explicit exports to ensure that we don't accidentally miss some test.-If we accidentally name some test in a way which will be ignored by `tasty-discover`, `weeder` will detect a useless export.--Some hints regarding `tasty` and our test-suite:-1. You can use `--hide-successes` to see only failing tests.-It's useful because otherwise if test suite fails you need to find the cause of failure manually.-2. However, beware of [this issue](https://github.com/feuerbach/tasty/issues/152) with `--hide-successes`.-In short, this option is somewhat broken when `tasty` thinks that it outputs to console.-A workaround is to set `TERM=dumb`.-3. You can run tests using our `Makefile`, see below.--## Cabal and Stack--We use [`hpack`](https://hackage.haskell.org/package/hpack) and `stack.yaml` to maintain the project-and its dependencies, but we also provide `.cabal` files in the repository due to-[stack issue](https://github.com/commercialhaskell/stack/issues/4906) which makes it impossible-to use `morley` as a dependency with stack version > 2 without `.cabal` files. Also we provide-`cabal.project` and `cabal.project.freeze` files in order to provide an ability to build the-project using `cabal`. If you want to update dependencies in one of these cabal related files you-should transfer changes to `package.yaml` or `stack.yaml` and run [`scripts/generate-cabal-files.sh`](scripts/generate-cabal-files.sh),-this script will update these files.--## Makefile--We have a [Makefile](/Makefile) which provides shortcuts for the most-common developers' activities, like building with flags suitable for-development, testing, applying `stylish-haskell` and `hlint`, building-Haddock documentation. Mentioned `Makefile` builds morley itself,-each extra package, like [`morley-ledgers`](/morley-ledgers/Makefile),-has its own `Makefile`.--If you want to run test suite with additional options, set `TEST_ARGUMENTS` variable.-Example: `TEST_ARGUMENTS="--pattern Parser" make test`.-If you want to enable `--hide-successes` option, you can use `make test-hide-successes`.-It will automatically set `TERM=dumb` which is a workaround for the issue mentioned earlier.--## Branching policy--Our branching policy is described [here](/docs/branching.md).--## Build recomendations--### Emacs intero--As soon as this repository uses different default extensions in different packages,-Intero may report non-existing errors.-This issue is known (once located under https://github.com/chrisdone/intero/issues/554)-and will never be resolved unless `intero` project is resurrected.--However, with proper care, this problem can be avoided. Just follow the rules:-* When working with `morley` package, select only `morley:*` packages in `intero-targets`.-* When developing on Lorentz within e. g. `morley-ledgers` package, make sure that-you first built morley core with `make morley`, only after that open the Lorentz modules.-Then set `morley-ledgers:*` packages as intero targets.
README.md view
@@ -9,13 +9,13 @@ It consists of the following parts: -- [`Tezos.*`](/src/Tezos/) hierarchy is designed to implement cryptographic primitives, string and byte formats, and any other functionality specific to the Tezos protocol which is required for testing/execution of Michelson contracts but is used not only by Michelson.-- [`Michelson.Untyped`](/src/Michelson/Untyped.hs) and [`Michelson.Typed`](src/Michelson/Typed.hs) hierarchies define Haskell data types that assemble a Michelson contract. See [michelsonTypes.md](/docs/michelsonTypes.md).-- [`Michelson.TypeCheck`](/src/Michelson/TypeCheck.hs): A typechecker that validates Michelson contracts according to the Michelson's typing rules. Essentially, it performs conversion from untyped representation to the typed one. See [morleyTypechecker.md](/docs/morleyTypechecker.md).-- [`Michelson.Interpret`](/src/Michelson/Interpret.hs): An interpreter for Michelson contracts which doesn't perform any side effects. See [morleyInterpreter.md](/docs/morleyInterpreter.md).-- [`Michelson.Macro`](/src/Michelson/Macro.hs) Types for macros, syntactic sugar, and other extensions that are described in the next chapter.-- [`Michelson.Parser`](/src/Michelson/Parser.hs) A parser to turn a `.tz` or `.mtz` file (`.mtz` is a Michelson contract with Morley extensions) into a Haskell ADT.-- [`Michelson.Runtime`](/src/Michelson/Runtime.hs): A high-level interface to Morley functionality, see [morleyRuntime.md](/docs/morleyRuntime.md).+- [`Tezos.*`](src/Tezos/) hierarchy is designed to implement cryptographic primitives, string and byte formats, and any other functionality specific to the Tezos protocol which is required for testing/execution of Michelson contracts but is used not only by Michelson.+- [`Michelson.Untyped`](src/Michelson/Untyped.hs) and [`Michelson.Typed`](src/Michelson/Typed.hs) hierarchies define Haskell data types that assemble a Michelson contract. See [michelsonTypes.md](/docs/michelsonTypes.md).+- [`Michelson.TypeCheck`](src/Michelson/TypeCheck.hs): A typechecker that validates Michelson contracts according to the Michelson's typing rules. Essentially, it performs conversion from untyped representation to the typed one. See [morleyTypechecker.md](/docs/morleyTypechecker.md).+- [`Michelson.Interpret`](src/Michelson/Interpret.hs): An interpreter for Michelson contracts which doesn't perform any side effects. See [morleyInterpreter.md](/docs/morleyInterpreter.md).+- [`Michelson.Macro`](src/Michelson/Macro.hs) Types for macros, syntactic sugar, and other extensions that are described in the next chapter.+- [`Michelson.Parser`](src/Michelson/Parser.hs) A parser to turn a `.tz` or `.mtz` file (`.mtz` is a Michelson contract with Morley extensions) into a Haskell ADT.+- [`Michelson.Runtime`](src/Michelson/Runtime.hs): A high-level interface to Morley functionality, see [morleyRuntime.md](/docs/morleyRuntime.md). ## II: Morley extensions @@ -69,31 +69,12 @@ + `stack exec -- morley --help` to see help message + `stack exec -- morley originate --contract contracts/tezos_examples/attic/add1.tz --storage 1 --verbose` - [Cabal](https://www.haskell.org/cabal/) based.- * Clone this git repository and run `cabal new-update && cabal new-build` command,- after that you can do `cabal run -- morley <args>` to run morley executable built from the source code.+ * Clone this git repository, go to this directory and run `cabal new-update && cabal new-build` command,+ after that you can do `cabal new-run -- morley <args>` to run morley executable built from the source code. * Usage example:- + `cabal run -- morley --help` to see help message- + `cabal run -- morley originate --contract contracts/tezos_examples/attic/add1.tz --storage 1 --verbose`+ + `cabal new-run -- morley --help` to see help message+ + `cabal new-run -- morley originate --contract contracts/tezos_examples/attic/add1.tz --storage 1 --verbose` For more information about Morley commands, check out the following docs: - [Interpreter doc](/docs/morleyInterpreter.md) - [Typechecker doc](/docs/morleyTypechecker.md)--## Michelson version--`master` and `production` branches are maintained to be compatible with version of Michelson running on mainnet.-We use separate branches to support other versions.-More information about our branching strategy can be found [here](/docs/branching.md).--## Issue Tracker--We used to use [YouTrack](https://issues.serokell.io/issues/TM) as our primary issue tracker.-You may see that commit messages up to some date are prefixed with `[TM-X]`.-This prefix refers to an issue in our YouTrack.-YouTrack issues are public, so you can open any of them.--Nowadays we are using built-in issue tracker on GitLab.--## For Contributors--Please see [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
app/Main.hs view
@@ -2,39 +2,35 @@ ( main ) where -import qualified Control.Exception as E import Data.Version (showVersion) import Fmt (pretty) import Named ((:!), (:?), arg, argF, (!)) import Options.Applicative- (auto, command, eitherReader, execParser, footerDoc, fullDesc, header, help, helper, info,- infoOption, long, maybeReader, metavar, option, progDesc, readerError, short, showDefault,- showDefaultWith, strOption, subparser, switch, value)+ (command, execParser, footerDoc, fullDesc, header, help, helper, info, infoOption, long,+ progDesc, short, subparser, switch) import qualified Options.Applicative as Opt import Options.Applicative.Help.Pretty (Doc, linebreak) import Paths_morley (version)-import System.Exit (ExitCode) import System.IO (utf8) import Text.Pretty.Simple (pPrint)-import qualified Text.Show import Michelson.Analyzer (analyze) import Michelson.Macro (expandContract) import Michelson.Optimizer (optimize)-import qualified Michelson.Parser as P import Michelson.Printer (printSomeContract, printUntypedContract) import Michelson.Runtime (TxData(..), originateContract, prepareContract, readAndParseContract, runContract, transfer, typeCheckWithDb) import Michelson.Runtime.GState (genesisAddress) import Michelson.TypeCheck.Types (SomeContract(..), mapSomeContract)-import Michelson.Typed (FullContract(..), epNameFromRefAnn)-import Michelson.Untyped hiding (OriginationOperation(..))+import Michelson.Typed (FullContract(..)) import qualified Michelson.Untyped as U-import Tezos.Address (Address, parseAddress)-import Tezos.Core- (Mutez, Timestamp(..), mkMutez, parseTimestamp, timestampFromSeconds, unMutez, unsafeMkMutez)+import Morley.CLI+import Tezos.Address (Address)+import Tezos.Core (Mutez, Timestamp(..), unsafeMkMutez) import Tezos.Crypto+import Util.CLI (outputOption)+import Util.Exception (displayUncaughtException) import Util.IO (hSetTranslit, withEncoding, writeFileUtf8) import Util.Named @@ -130,7 +126,7 @@ printSubCmd = mkCommandParser "print"- (Print <$> (#input <.?> contractFileOption)+ (Print <$> (#input <.?> optional contractFileOption) <*> (#output <.?> outputOption) <*> (#singleLine <.!> onelineOption)) ("Parse a Morley contract and print corresponding Michelson " <>@@ -180,13 +176,13 @@ typeCheckOptions :: Opt.Parser TypeCheckOptions typeCheckOptions = TypeCheckOptions- <$> contractFileOption+ <$> optional contractFileOption <*> dbPathOption <*> verboseFlag parseOptions :: Opt.Parser (Maybe FilePath, Bool) parseOptions = (,)- <$> contractFileOption+ <$> optional contractFileOption <*> switch ( long "expand-macros" <> help "Whether expand macros after parsing or not")@@ -196,215 +192,60 @@ optimizeOptions :: Opt.Parser OptimizeOptions optimizeOptions = OptimizeOptions- <$> contractFileOption+ <$> optional contractFileOption <*> dbPathOption <*> outputOption <*> onelineOption analyzeOptions :: Opt.Parser AnalyzeOptions analyzeOptions = AnalyzeOptions- <$> contractFileOption+ <$> optional contractFileOption <*> dbPathOption runOptions :: Opt.Parser RunOptions runOptions = RunOptions- <$> contractFileOption+ <$> optional contractFileOption <*> dbPathOption- <*> valueOption "storage" "Initial storage of a running contract"- <*> txData+ <*> valueOption Nothing (#name .! "storage")+ (#help .! "Initial storage of a running contract")+ <*> txDataOption <*> verboseFlag <*> nowOption <*> maxStepsOption <*> mutezOption (Just defaultBalance)- "balance" "Initial balance of this contract"+ (#name .! "balance") (#help .! "Initial balance of this contract") <*> writeFlag originateOptions :: Opt.Parser OriginateOptions originateOptions = OriginateOptions- <$> contractFileOption+ <$> optional contractFileOption <*> dbPathOption- <*> addressOption (Just genesisAddress) "originator" "Contract's originator"+ <*> addressOption (Just genesisAddress)+ (#name .! "originator") (#help .! "Contract's originator") <*> optional- (keyHashOption Nothing "delegate" "Contract's optional delegate")- <*> valueOption "storage" "Initial storage of an originating contract"+ (keyHashOption Nothing+ (#name .! "delegate") (#help .! "Contract's optional delegate")+ )+ <*> valueOption Nothing+ (#name .! "storage") (#help .! "Initial storage of an originating contract") <*> mutezOption (Just defaultBalance)- "balance" "Initial balance of an originating contract"+ (#name .! "balance") (#help .! "Initial balance of an originating contract") <*> verboseFlag transferOptions :: Opt.Parser TransferOptions transferOptions = do toDBPath <- dbPathOption- toDestination <- addressOption Nothing "to" "Destination address"- toTxData <- txData+ toDestination <-+ addressOption Nothing+ (#name .! "to") (#help .! "Destination address")+ toTxData <- txDataOption toNow <- nowOption toMaxSteps <- maxStepsOption toVerbose <- verboseFlag toDryRun <- dryRunFlag pure TransferOptions {..}---contractFileOption :: Opt.Parser (Maybe FilePath)-contractFileOption = optional $ strOption $- long "contract" <>- metavar "FILEPATH" <>- help "Path to contract file"--nowOption :: Opt.Parser (Maybe Timestamp)-nowOption = optional $ option parser $- long "now" <>- metavar "TIMESTAMP" <>- help "Timestamp that you want the runtime interpreter to use (default is now)"- where- parser =- (timestampFromSeconds <$> auto) <|>- maybeReader (parseTimestamp . toText)--maxStepsOption :: Opt.Parser Word64-maxStepsOption = option auto $- value 100500 <>- long "max-steps" <>- metavar "INT" <>- help "Max steps that you want the runtime interpreter to use" <>- showDefault--dbPathOption :: Opt.Parser FilePath-dbPathOption = strOption $- long "db" <>- metavar "FILEPATH" <>- value "db.json" <>- help "Path to DB with data which is used instead of real blockchain data" <>- showDefault--keyHashOption :: Maybe KeyHash -> String -> String -> Opt.Parser KeyHash-keyHashOption defaultValue name hInfo =- option (eitherReader (first pretty . parseKeyHash . toText)) $- long name <>- maybeAddDefault pretty defaultValue <>- help hInfo--valueOption :: String -> String -> Opt.Parser U.Value-valueOption name hInfo = option (eitherReader parseValue) $- long name <>- help hInfo- where- parseValue :: String -> Either String U.Value- parseValue =- first (mappend "Failed to parse value: " . displayException) .- P.parseExpandValue .- toText--mutezOption :: Maybe Mutez -> String -> String -> Opt.Parser Mutez-mutezOption defaultValue name hInfo =- option (maybe (readerError "Invalid mutez") pure . mkMutez =<< auto) $- long name <>- metavar "INT" <>- maybeAddDefault (show . unMutez) defaultValue <>- help hInfo--addressOption :: Maybe Address -> String -> String -> Opt.Parser Address-addressOption defAddress name hInfo =- option (eitherReader parseAddrDo) $ mconcat- [ long name- , metavar "ADDRESS"- , help hInfo- , maybeAddDefault pretty defAddress- ]- where- parseAddrDo addr =- either (Left . mappend "Failed to parse address: " . pretty) Right $- parseAddress $ toText addr--onelineOption :: Opt.Parser Bool-onelineOption = switch (- long "oneline" <>- help "Force single line output")--outputOption :: Opt.Parser (Maybe FilePath)-outputOption = optional . strOption $- short 'o' <>- long "output" <>- metavar "FILEPATH" <>- help "Write output to the given file. If not specified, stdout is used."--entrypointOption :: String -> String -> Opt.Parser EpName-entrypointOption name hInfo =- option (eitherReader parseEntrypointDo) $ mconcat- [ long name- , metavar "ENTRYPOINT"- , help hInfo- , value DefEpName- , showDefaultWith pretty- ]- where- parseEntrypointDo (toText -> txt) = do- an <-- mkAnnotation txt- & first (mappend "Failed to parse entrypoint: " . pretty)- epNameFromRefAnn an- & first pretty--txData :: Opt.Parser TxData-txData =- mkTxData- <$> addressOption (Just genesisAddress) "sender" "Sender address"- <*> valueOption "parameter" "Parameter of passed contract"- <*> mutezOption (Just minBound) "amount" "Amout sent by a transaction"- <*> entrypointOption "entrypoint" "Entrypoint to call"- where- mkTxData :: Address -> Value -> Mutez -> EpName -> TxData- mkTxData addr param amount epName =- TxData- { tdSenderAddress = addr- , tdParameter = param- , tdEntrypoint = epName- , tdAmount = amount- }---- Maybe add default value and make sure it will be shown in help message.-maybeAddDefault :: Opt.HasValue f => (a -> String) -> Maybe a -> Opt.Mod f a-maybeAddDefault printer = maybe mempty addDefault- where- addDefault v = value v <> showDefaultWith printer--------------------------------------------------------------------------------- Better printing of exceptions-------------------------------------------------------------------------------newtype DisplayExceptionInShow = DisplayExceptionInShow SomeException--instance Show DisplayExceptionInShow where- show (DisplayExceptionInShow se) = displayException se--instance Exception DisplayExceptionInShow---- | Customise default uncaught exception handling. The problem with--- the default handler is that it uses `show` to display uncaught--- exceptions, but `displayException` may provide more reasonable--- output. We do not modify uncaught exception handler, but simply--- wrap uncaught exceptions (only synchronous ones) into--- 'DisplayExceptionInShow'.------ Some exceptions (currently we are aware only of 'ExitCode') are--- handled specially by default exception handler, so we don't wrap--- them.-displayUncaughtException :: IO () -> IO ()-displayUncaughtException = mapIOExceptions wrapUnlessExitCode- where- -- We can't use `mapException` here, because it only works with- -- exceptions inside pure values, not with `IO` exceptions.- -- Note: it doesn't catch async exceptions.- mapIOExceptions :: (SomeException -> SomeException) -> IO a -> IO a- mapIOExceptions f action = action `catchAny` (E.throwIO . f)-- -- We don't wrap `ExitCode` because it seems to be handled specially.- -- Application exit code depends on the value stored in `ExitCode`.- wrapUnlessExitCode :: SomeException -> SomeException- wrapUnlessExitCode e =- case fromException @ExitCode e of- Just _ -> e- Nothing -> toException $ DisplayExceptionInShow e ---------------------------------------------------------------------------- -- Actual main
+ bench/Main.hs view
@@ -0,0 +1,70 @@+module Main (main) where++import qualified Data.Map as M+import Gauge.Main (bench, nf, defaultMain, bgroup)+import Text.Megaparsec (parse)++import Michelson.Interpret (interpret)+import Michelson.Parser as P+import Michelson.Runtime (prepareContract)+import Michelson.Test.Import+import Michelson.Test.Dummy+import Michelson.Text+import Michelson.TypeCheck as T+import Michelson.Typed as T+import Tezos.Address+import Util.IO++main :: IO ()+main = do+ let+ basicFp = "../../contracts/basic1.tz"+ stringCallerFp = "../../contracts/string_caller.tz"+ callSelfFp = "../../contracts/call_self_several_times.tz"+ sq2Fp = "../../contracts/testassert_square2.mtz"+ contractPaths = [basicFp, stringCallerFp, callSelfFp, sq2Fp]+ contracts <- traverse (\x -> (x,) <$> readFileUtf8 x) contractPaths+ let makeParseBench (filename, code) =+ bench filename $ nf (parse P.program filename) code+ preparedContracts <- evaluateNF =<< traverse (\x -> (x,) <$> prepareContract (Just x)) contractPaths+ let+ makeTypeCheckBench (filename, contract) = bench filename $+ nf (T.typeCheckContract (M.fromList [])) contract++ (_, basicC) <- importContract basicFp+ (_, stringCallerC) <- importContract stringCallerFp+ (_, callSelfC) <- importContract callSelfFp+ (_, sq2C) <- importContract sq2Fp+ let+ basicBench = bench basicFp+ (nf+ (interpret (T.fcCode basicC) T.epcPrimitive T.VUnit (T.VList [T.VC (T.CvInt 0)]))+ dummyContractEnv+ )++ dummyAddress = detGenKeyAddress "thegreatandpowerful"+ dummyString = mkMTextUnsafe "TGAP"+ stringCallerBench = bench stringCallerFp+ (nf+ (interpret (T.fcCode stringCallerC) T.epcPrimitive (T.toVal dummyString) (T.toVal dummyAddress))+ dummyContractEnv+ )+ callSelfBench = bench callSelfFp+ (nf+ (interpret (T.fcCode callSelfC) T.epcPrimitive (T.toVal (100 :: Integer)) (T.toVal (0 :: Natural)))+ dummyContractEnv+ )++ sq2Bench = bench sq2Fp+ (nf+ (interpret (T.fcCode sq2C) T.epcPrimitive (T.toVal (100 :: Integer, 200 :: Integer)) T.VUnit)+ dummyContractEnv+ )++++ defaultMain+ [ bgroup "parsing" $ map makeParseBench contracts+ , bgroup "type-checking" $ map makeTypeCheckBench preparedContracts+ , bgroup "interpreting" $ [basicBench, stringCallerBench, callSelfBench, sq2Bench]+ ]
morley.cabal view
@@ -1,13 +1,13 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.32.0.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: 0e0daabc72a430afec07d1e179157ea8879f37c1581d88a8ce1fd0bd1410264d+-- hash: c1daa2d2e51ff7183ee4cf60a81aeda135b69cb2410f30184efbebe64c69710c name: morley-version: 1.0.0+version: 1.1.0 synopsis: Developer tools for the Michelson Language description: A library to make writing smart contracts in Michelson — the smart contract language of the Tezos blockchain — pleasant and effective. category: Language@@ -21,7 +21,6 @@ build-type: Simple extra-source-files: CHANGES.md- CONTRIBUTING.md README.md source-repository head@@ -109,6 +108,8 @@ Michelson.Untyped.Instr Michelson.Untyped.Type Michelson.Untyped.Value+ Morley.CLI+ Morley.Micheline Tezos.Address Tezos.Core Tezos.Crypto@@ -119,12 +120,14 @@ Tezos.Crypto.Util Util.Alternative Util.ByteString+ Util.CLI Util.Default Util.Exception Util.Fcf Util.Generic Util.Instances Util.IO+ Util.Label Util.Lens Util.Markdown Util.Named@@ -133,6 +136,7 @@ Util.Test.Arbitrary Util.Test.Ingredients Util.Text+ Util.TH Util.Type Util.Typeable Util.TypeLits@@ -145,7 +149,7 @@ hs-source-dirs: src default-extensions: ApplicativeDo AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances ViewPatterns- ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -O0+ ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude build-depends: HUnit , QuickCheck@@ -168,6 +172,7 @@ , gitrev , hex-text , hspec+ , hspec-expectations , interpolate , lens , megaparsec >=7.0.0@@ -175,6 +180,7 @@ , morley-prelude >=0.3.0 , mtl , named+ , optparse-applicative , parser-combinators >=1.0.0 , quickcheck-arbitrary-adt , quickcheck-instances@@ -187,6 +193,7 @@ , tasty-quickcheck , template-haskell , text+ , tezos-bake-monitor-lib , th-lift , th-lift-instances , time@@ -207,7 +214,7 @@ hs-source-dirs: app default-extensions: ApplicativeDo AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances ViewPatterns- ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -O0+ ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude build-depends: base-noprelude >=4.7 && <5 , fmt@@ -230,6 +237,7 @@ Test.Interpreter Test.Interpreter.Apply Test.Interpreter.Auction+ Test.Interpreter.Balance Test.Interpreter.CallSelf Test.Interpreter.Compare Test.Interpreter.ComparePairs@@ -266,7 +274,7 @@ hs-source-dirs: test default-extensions: ApplicativeDo AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances ViewPatterns DerivingStrategies- ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -O0 -threaded -with-rtsopts=-N+ ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -threaded -with-rtsopts=-N build-tool-depends: tasty-discover:tasty-discover build-depends:@@ -298,4 +306,22 @@ , tasty-quickcheck , text , unordered-containers+ default-language: Haskell2010++benchmark morley-bench+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ Paths_morley+ hs-source-dirs:+ bench+ default-extensions: ApplicativeDo AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances ViewPatterns+ ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude+ build-depends:+ base-noprelude >=4.7 && <5+ , containers+ , gauge+ , megaparsec >=7.0.0+ , morley+ , morley-prelude default-language: Haskell2010
src/Michelson/Doc.hs view
@@ -29,6 +29,7 @@ , docGroupContent , docDefinitionRef + , DGeneralInfoSection (..) , DName (..) , DDescription (..) , DGitRevision (..)@@ -36,6 +37,7 @@ , mkDGitRevision , morleyRepoSettings , DComment (..)+ , DAnchor (..) ) where import qualified Data.Map as M@@ -167,7 +169,7 @@ manchor <> docItemToMarkdown l d <> "\n\n" where manchor = case docItemRef d of- DocItemRef (DocItemId docItemId) -> mdAnchor docItemId+ DocItemRef docItemId -> mdAnchor docItemId DocItemNoRef -> "" -- | Order items by their 'docItemId'.@@ -184,13 +186,14 @@ :: (DocItem d, DocItemPlacement d ~ 'DocItemInDefinitions) => Markdown -> d -> Markdown docDefinitionRef refText d = case docItemRef d of- DocItemRef (DocItemId docItemId) -> mdLocalRef refText docItemId+ DocItemRef docItemId -> mdLocalRef refText docItemId -- | Some unique identifier of a doc item. -- -- All doc items which should be refer-able need to have this identifier. newtype DocItemId = DocItemId Text deriving stock (Eq, Ord, Show)+ deriving newtype (ToAnchor) -- | Position of all doc items of some type. newtype DocItemPos = DocItemPos Natural@@ -220,6 +223,12 @@ data SomeDocItem where SomeDocItem :: DocItem d => d -> SomeDocItem +-- NFData instance is needed for benchmarks and we want to avoid requiring users+-- to implement NFData instance for every single DocItem and they should not+-- affect the performance anyway.+instance NFData SomeDocItem where+ rnf (SomeDocItem _) = ()+ -- | Hides some documentation item which is put to "definitions" section. data SomeDocDefinitionItem where SomeDocDefinitionItem@@ -316,10 +325,13 @@ case sectionDesc of Nothing -> "" Just sd -> sd <> "\n\n"+ resItems = docItemsOrder $ map deItem (toList items) content =- mconcat $ docItemsOrder (map deItem $ toList items) <&> \di ->+ mconcat $ resItems <&> \di -> docItemToMarkdownFull (headerLevelDelta hl) di- in sectionNameFull <> sectionDescFull <> content+ in if null resItems+ then ""+ else sectionNameFull <> sectionDescFull <> content -- | Lift a doc item to a block, be it atomic doc item or grouping one. docItemToBlockGeneral :: forall di. DocItem di => di -> Maybe SubDoc -> DocBlock@@ -447,11 +459,23 @@ -- Basic doc items ---------------------------------------------------------------------------- +-- | General (meta-)information about the contract such as git+-- revision, contract's authors, etc. Should be relatively short (not+-- several pages) because it is put somewhere close to the beginning of+-- documentation.+newtype DGeneralInfoSection = DGeneralInfoSection SubDoc++instance DocItem DGeneralInfoSection where+ type DocItemPosition DGeneralInfoSection = 1+ docItemSectionName = Nothing+ docItemToMarkdown lvl (DGeneralInfoSection subDoc) =+ subDocToMarkdown lvl subDoc+ -- | Give a name to document block. data DName = DName Text SubDoc instance DocItem DName where- type DocItemPosition DName = 1+ type DocItemPosition DName = 3 docItemSectionName = Nothing docItemToMarkdown lvl (DName name doc) = mdHeader lvl (build name) <>@@ -522,10 +546,10 @@ value -> error $ "Unknown value returned by git: " <> show value instance DocItem DGitRevision where- type DocItemPosition DGitRevision = 7+ type DocItemPosition DGitRevision = 2 docItemSectionName = Nothing docItemToMarkdown _ (DGitRevisionKnown DGitRevisionInfo{..}) =- mconcat $+ mconcat [ mdSubsection "Code revision" $ let link = grsMkGitRevision dgrRepoSettings dgrCommitSha in mconcat@@ -536,7 +560,7 @@ ] docItemToMarkdown _ DGitRevisionUnknown = "" --- | Comment in the doc (mostly used for licenses)+-- | Comment in the doc (mostly used for licenses) data DComment = DComment Text instance DocItem DComment where@@ -544,3 +568,11 @@ docItemSectionName = Nothing docItemToMarkdown _ (DComment commentText) = "<!---\n" +| commentText |+ "\n-->"++-- | A hand-made anchor.+data DAnchor = DAnchor Anchor++instance DocItem DAnchor where+ type DocItemPosition DAnchor = 4+ docItemSectionName = Nothing+ docItemToMarkdown _ (DAnchor a) = mdAnchor a
src/Michelson/Doc/Test.hs view
@@ -16,6 +16,7 @@ -- ** Individual test predicates , testContractNameAtTop+ , testNoGitInfo , testDocNotEmpty , testNoAdjacentDescriptions @@ -24,6 +25,7 @@ , forEachContractDocItem ) where +import qualified Data.List as L import qualified Data.Text as T import Data.Text.Lazy.Builder (toLazyText) import Fmt (Buildable(..), blockListF, fmt, nameF, pretty)@@ -61,6 +63,9 @@ build DocTest{..} = "Doc test '" <> build dtDesc <> "'" -- | Construct 'DocTest'.+--+-- Note: you should not declare helpers with this function rather use it+-- directly in every test suite. mkDocTest :: HasCallStack => String@@ -74,9 +79,16 @@ -- | Exclude given test suite. excludeDocTest :: HasCallStack => DocTest -> [DocTest] -> [DocTest]-excludeDocTest toExclude tests- | toExclude `elem` tests = filter (/= toExclude) tests- | otherwise = error $ "Not in the list of doc items: " <> pretty toExclude+excludeDocTest toExclude tests =+ case L.partition (== toExclude) tests of+ ([], _) ->+ error $ "Not in the list of doc items: " <> pretty toExclude+ (_ : _ : _, _) ->+ -- This is possible if someone abused 'mkDocTest' and created a function+ -- which calls it and this function is used to create multiple predicates+ error "Running invalid doc predicates: multiple ones were considered equal"+ ([_], notExcluded) ->+ notExcluded -- | Calling @excludeDocTests tests toExclude@ returns all test suites from -- @tests@ which are not present in @toExclude@.@@ -136,10 +148,24 @@ testContractNameAtTop :: DocTest testContractNameAtTop = mkDocTest "The whole contract is wrapped into 'DName'" $- \contractDoc ->- assertBool "There is no 'DName' at the top" . isJust $- lookupDocBlockSection @DName (cdContents contractDoc)+ \contractDoc -> do+ let mSections = lookupDocBlockSection @DName (cdContents contractDoc)+ case mSections of+ Nothing -> assertFailure "There is no 'DName' at the top"+ Just _ -> return () +-- | Check that contracts themselves do not set the git revision. It is supposed to be filled only+-- in the executable.+testNoGitInfo :: DocTest+testNoGitInfo =+ mkDocTest "Git revision is not set in the contract" $+ \contractDoc -> do+ assertions <- sequence $ forEachContractDocItem contractDoc $ \case+ DGitRevisionUnknown -> return ()+ _ -> assertFailure "Unexpected Git revision"+ assertBool "No Git revision placeholder found or more than one" $+ (length assertions == 1)+ -- | Check that there is at least one non-grouping doc item. -- -- If there is no such, rendered documentation will be empty which signals about@@ -196,6 +222,7 @@ testDocBasic :: [DocTest] testDocBasic = [ testContractNameAtTop+ , testNoGitInfo , testDocNotEmpty , testNoAdjacentDescriptions , testDescriptionsAreWellFormatted
src/Michelson/ErrorPos.hs view
@@ -8,14 +8,16 @@ , LetName (..) ) where +import qualified Data.Aeson as Aeson import Data.Data (Data(..))+import Data.Default (Default(..)) import qualified Data.Text as T-import Data.Default (Default (..))-import qualified Data.Aeson as Aeson newtype Pos = Pos Word deriving stock (Eq, Ord, Show, Generic, Data) +instance NFData Pos+ mkPos :: Int -> Pos mkPos x | x < 0 = error $ "negative pos: " <> show x@@ -24,17 +26,23 @@ data SrcPos = SrcPos Pos Pos deriving stock (Eq, Ord, Show, Generic, Data) +instance NFData SrcPos+ srcPos :: Word -> Word -> SrcPos srcPos x y = SrcPos (Pos x) (Pos y) newtype LetName = LetName T.Text deriving stock (Eq, Ord, Show, Data, Generic) +instance NFData LetName+ type LetCallStack = [LetName] data InstrCallStack = InstrCallStack { icsCallStack :: LetCallStack , icsSrcPos :: SrcPos } deriving stock (Eq, Ord, Show, Generic, Data)++instance NFData InstrCallStack instance Default Pos where def = Pos 0
src/Michelson/Interpret.hs view
@@ -6,27 +6,33 @@ , MichelsonFailed (..) , RemainingSteps (..) , SomeItStack (..)- , EvalOp , MorleyLogs (..) , noMorleyLogs , interpret , interpretInstr , ContractReturn- , handleContractReturn -+ , 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 (throwError)+import Control.Monad.Except (MonadError, throwError) import Data.Default (Default(..)) import qualified Data.Map as Map import qualified Data.Set as Set@@ -38,16 +44,17 @@ import Michelson.Interpret.Pack (packValue') import Michelson.Interpret.Unpack (UnpackError, unpackValue') import Michelson.TypeCheck- (SomeContract(..), SomeNotedValue(..), TCError, TCTypeError(..), TcOriginatedContracts, eqType,- matchTypes, runTypeCheck, typeCheckContract, typeCheckValue)+ (SomeContract(..), TCError, TcOriginatedContracts, matchTypes, runTypeCheck,+ typeCheckContract, typeCheckValue) import Michelson.Typed import qualified Michelson.Typed as T-import Michelson.Typed.Convert (convertContract, untypeValue)+import Michelson.Typed.Convert (convertContractCode, untypeValue) 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, Sing(SS, SZ))+import Util.TH import Util.Type import Util.Typeable @@ -122,8 +129,6 @@ | IllTypedContract TCError | IllTypedParam TCError | IllTypedStorage TCError- | UnexpectedParamType TCTypeError- | UnexpectedStorageType TCTypeError deriving stock (Generic) deriving stock instance Show InterpretError@@ -153,12 +158,15 @@ , iurNewState = st } --- | Morley interpreter state+-- | Morley logs for interpreter state. newtype MorleyLogs = MorleyLogs { unMorleyLogs :: [Text]- } deriving stock (Eq, Show)+ -- ^ Logs in reverse order.+ } deriving stock (Eq, Show, Generic) deriving newtype (Default, Buildable) +instance NFData MorleyLogs+ noMorleyLogs :: MorleyLogs noMorleyLogs = MorleyLogs [] @@ -172,20 +180,19 @@ -> ContractEnv -> Either InterpretError InterpretResult interpretUntyped uContract@U.Contract{..} paramU initStU env = do- SomeContract (FullContract (instr :: Contract cp st) _ _)+ SomeContract (FullContract (instr :: ContractCode cp st) _ _) <- first IllTypedContract $ typeCheckContract (ceContracts env) uContract- withUType para $ \(sgp, ntp) ->- withUType stor $ \(sgs, nts) -> do- paramV :::: ((_ :: Sing cp1), _)- <- first IllTypedParam $ runTypeCheck para (ceContracts env) $ usingReaderT def $- typeCheckValue paramU (sgp, ntp)- initStV :::: ((_ :: Sing st1), _)- <- first IllTypedStorage $ runTypeCheck para (ceContracts env) $ usingReaderT def $- typeCheckValue initStU (sgs, nts)- Refl <- first UnexpectedStorageType $ eqType @st @st1- Refl <- first UnexpectedParamType $ eqType @cp @cp1- handleContractReturn $- interpret instr epcCallRootUnsafe paramV initStV env+ -- 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+ paramV+ <- first IllTypedParam $ runTypeCheck para (ceContracts env) $ usingReaderT def $+ typeCheckValue @cp paramU+ initStV+ <- first IllTypedStorage $ runTypeCheck para (ceContracts env) $ usingReaderT def $+ typeCheckValue @st initStU+ handleContractReturn $+ interpret instr epcCallRootUnsafe paramV initStV env type ContractReturn st = (Either MichelsonFailed ([Operation], T.Value st), InterpreterState)@@ -194,43 +201,43 @@ :: (StorageScope st) => ContractReturn st -> Either InterpretError InterpretResult handleContractReturn (ei, s) =- bimap RuntimeFailure constructIR $- bimap (,isMorleyLogs s) (,s) ei+ bimap (RuntimeFailure . (, isMorleyLogs s)) (constructIR . (, s)) ei interpret' :: forall cp st arg.- Contract cp st+ 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 toRes) $+interpret' instr epc param initSt env ist = first (fmap fromFinalStack) $ runEvalOp- (runInstr instr (T.VPair (liftParam param, initSt) :& RNil))+ (runInstr instr $ mkInitStack (liftCallArg epc param) initSt) env ist- where- liftParam :: T.Value arg -> T.Value cp- liftParam = compileEpLiftSequence (epcLiftSequence epc) - toRes- :: (Rec (T.Value' instr) '[ 'TPair ('TList 'TOperation) st ])- -> ([T.Operation' instr], T.Value' instr st)- toRes (T.VPair (T.VList ops_, newSt) :& RNil) =- (map (\(T.VOp op) -> op) ops_, newSt)+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- :: Contract cp st+ :: 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 (InterpreterState def $ ceMaxSteps 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. --@@ -251,32 +258,45 @@ SomeItStack :: T.ExtInstr inp -> Rec T.Value inp -> SomeItStack newtype RemainingSteps = RemainingSteps Word64- deriving stock (Show)+ deriving stock (Show, Generic) deriving newtype (Eq, Ord, Buildable, Num) +instance NFData RemainingSteps+ data InterpreterState = InterpreterState { isMorleyLogs :: MorleyLogs , isRemainingSteps :: RemainingSteps- } deriving stock (Show)+ } deriving stock (Show, Generic) +instance NFData InterpreterState+ type EvalOp a = ExceptT MichelsonFailed (ReaderT ContractEnv (State InterpreterState)) a -runEvalOp ::- EvalOp a+runEvalOp+ :: EvalOp a -> ContractEnv -> InterpreterState -> (Either MichelsonFailed a, InterpreterState) runEvalOp act env initSt = flip runState initSt $ usingReaderT env $ runExceptT act --- | Function to change amount of remaining steps stored in State monad-runInstr- :: Instr inp out+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- -> EvalOp (Rec (T.Value) out)+ -> 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@@ -290,23 +310,11 @@ modify (\s -> s {isRemainingSteps = rs - 1}) runInstrImpl runInstr i r -runInstrNoGas- :: forall a b . T.Instr a b -> Rec T.Value a -> EvalOp (Rec T.Value b)+runInstrNoGas :: EvalM m => InstrRunner m runInstrNoGas = runInstrImpl runInstrNoGas - -- | Function to interpret Michelson instruction(s) against given stack.-runInstrImpl- :: (forall inp1 out1 .- Instr inp1 out1- -> Rec (T.Value) inp1- -> EvalOp (Rec T.Value out1)- ) ->- (forall inp out .- Instr inp out- -> Rec (T.Value) inp- -> EvalOp (Rec T.Value out)- )+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@@ -375,12 +383,13 @@ runInstrImpl runner (MAP ops) (a :& r) = case ops of (code :: Instr (MapOpInp c ': s) (b ': s)) -> do- newList <- mapM (\(val :: T.Value (MapOpInp c)) -> do- res <- runner code (val :& r)+ -- 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- ((newVal :: T.Value b) :& _) -> pure newVal)- $ mapOpToList @c a- pure $ mapOpFromList a newList :& r+ ((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) ->@@ -475,9 +484,9 @@ 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@(AsUType tcSing (ParamNotesUnsafe -> tcNotes)) ->+ Just tc@(AsUTypeExt tcSing (ParamNotesUnsafe -> tcNotes)) -> case (T.checkOpPresence tcSing, T.checkNestedBigMapsPresence tcSing) of- (OpAbsent, NestedBigMapsAbsent) -> castContract addr epName (tcSing, tcNotes) :& r+ (OpAbsent, NestedBigMapsAbsent) -> castContract addr epName tcNotes :& r _ -> error $ "Illegal type in parameter of env contract: " <> pretty tc -- TODO [#36]: we can do this safely once 'TcOriginatedContracts' stores -- typed stuff.@@ -485,7 +494,7 @@ where castContract :: forall p. T.ParameterScope p- => Address -> EpName -> (Sing p, T.ParamNotes p) -> T.Value ('TOption ('TContract a))+ => 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@@ -542,11 +551,11 @@ -- | Evaluates an arithmetic operation and either fails or proceeds. runArithOp- :: (ArithOp aop n m, Typeable n, Typeable m)+ :: (ArithOp aop n m, Typeable n, Typeable m, EvalM monad) => proxy aop -> CValue n -> CValue m- -> EvalOp (T.Value' instr ('Tc (ArithRes aop n m)))+ -> monad (T.Value' instr ('Tc (ArithRes aop n m))) runArithOp op l r = case evalOp op l r of Left err -> throwError (MichelsonArithError err) Right res -> pure (T.VC res)@@ -567,7 +576,7 @@ => Address -> Maybe (T.Value ('Tc 'U.CKeyHash)) -> Mutez- -> Contract param store+ -> ContractCode param store -> Value' Instr store -> U.OriginationOperation createOrigOp originator mbDelegate bal contract g =@@ -576,13 +585,13 @@ , ooDelegate = unwrapMbKeyHash mbDelegate , ooBalance = bal , ooStorage = untypeValue g- , ooContract = convertContract contract+ , ooContract = convertContractCode contract } unwrapMbKeyHash :: Maybe (T.Value ('Tc 'U.CKeyHash)) -> Maybe KeyHash unwrapMbKeyHash mbKeyHash = mbKeyHash <&> \(T.VC (CvKeyHash keyHash)) -> keyHash -interpretExt :: SomeItStack -> EvalOp ()+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@@ -613,3 +622,5 @@ loop = \case (e :& _, SZ) -> cont e (_ :& es, SS n) -> loop (es, n)++$(deriveGADTNFData ''MichelsonFailed)
src/Michelson/Interpret/Pack.hs view
@@ -26,7 +26,7 @@ import Michelson.Text import Michelson.Typed import Michelson.Untyped.Annotation- (Annotation(..), FieldAnn, TypeAnn, VarAnn, noAnn, renderWEAnn)+ (Annotation(..), FieldAnn, TypeAnn, VarAnn, fullAnnSet, isNoAnnSet, noAnn) import Tezos.Address (Address(..), ContractHash(..)) import Tezos.Core (ChainId(..), Mutez(..), timestampToSeconds) import Tezos.Crypto (KeyHash(..), KeyHashTag(..), PublicKey(..), signatureToBytes)@@ -52,9 +52,6 @@ packCode' :: Instr inp out -> ByteString packCode' = LBS.toStrict . encodeInstrs -surround :: LByteString -> LByteString -> ByteString -> LByteString-surround prefix suffix main = prefix <> LBS.fromStrict main <> suffix- -- | Generic serializer. -- -- We don't require @HasNoBigMap@ constraint here since the big_map serialization@@ -143,8 +140,10 @@ encodeAddress :: Address -> LByteString encodeAddress = \case- KeyAddress keyHash -> "\x00" <> (encodeKeyHashRaw keyHash)- ContractAddress (ContractHash address) -> surround "\x01" "\x00" address+ KeyAddress keyHash ->+ "\x00" <> (encodeKeyHashRaw keyHash)+ ContractAddress (ContractHash address) ->+ "\x01" <> LBS.fromStrict address <> "\x00" encodeEpAddress :: EpAddress -> LByteString encodeEpAddress (EpAddress addr epName) =@@ -393,15 +392,11 @@ encodeWithAnns :: [TypeAnn] -> [FieldAnn] -> [VarAnn] -> LByteString -> LByteString encodeWithAnns tns fns vns encodedInput | null encodedInput = encodedInput- | null annsList = encodedInput+ | isNoAnnSet annSet = encodedInput | otherwise = inputIncrem <> encodedAnns where- trimEndNoAnn a lst = if null lst && a == noAnn then [] else a : lst- tnsText = map (show . renderWEAnn) $ foldr trimEndNoAnn [] tns- fnsText = map (show . renderWEAnn) $ foldr trimEndNoAnn [] fns- vnsText = map (show . renderWEAnn) $ foldr trimEndNoAnn [] vns- annsList = tnsText <> fnsText <> vnsText- encodedAnns = encodeAsList . encodeUtf8 $ unwords annsList+ annSet = fullAnnSet tns fns vns+ encodedAnns = encodeAsList . encodeUtf8 $ show @Text annSet inputIncrem = (1 + LBS.head encodedInput) `LBS.cons` LBS.tail encodedInput -- | Encode an instruction with variable annotations@@ -413,7 +408,7 @@ -- | Encode an instruction with Annotations encodeNotedInstr :: forall inp out. Instr inp out -> PackedNotes out -> [VarAnn] -> LByteString-encodeNotedInstr a (PackedNotes n _) vns = case (a, Proxy @out, n) of+encodeNotedInstr a (PackedNotes n) vns = case (a, Proxy @out, n) of (SOME, _, NTOption tn _ns) -> encodeWithAnns [tn] [] vns $ encodeInstr a (NONE, _ :: Proxy ('TOption t ': s), NTOption tn ns) ->
src/Michelson/Interpret/Unpack.hs view
@@ -480,7 +480,7 @@ -> m (RemFail T.Instr '[inp] '[out]) decodeTypeCheckLam uinstr = either tcErrToFail pure . evaluatingState tcInitEnv . runExceptT $ do- let inp = (sing @inp, starNotes, noAnn) ::& SNil+ let inp = (starNotes, noAnn) ::& SNil _ :/ instr' <- typeCheckList uinstr inp case instr' of instr ::: out' ->
src/Michelson/Macro.hs view
@@ -54,11 +54,15 @@ instance Buildable LetMacro where build = genericF +instance NFData LetMacro+ data PairStruct = F (VarAnn, FieldAnn) | P PairStruct PairStruct deriving stock (Eq, Show, Data, Generic) +instance NFData PairStruct+ instance Buildable PairStruct where build = genericF @@ -67,6 +71,8 @@ | D deriving stock (Eq, Show, Data, Generic) +instance NFData CadrStruct+ instance Buildable CadrStruct where build = genericF @@ -90,6 +96,8 @@ LMac letMacro _ -> "<LMac: "+|letMacro|+">" Seq parsedOps _ -> "<Seq: "+|parsedOps|+">" +instance NFData ParsedOp+ ------------------------------------- -- Types produced by parser -------------------------------------@@ -160,6 +168,8 @@ ASSERT_RIGHT -> "ASSERT_RIGHT" IF_SOME parsedOps1 parsedOps2 -> "<IF_SOME: "+|parsedOps1|+", "+|parsedOps2|+">" IF_RIGHT parsedOps1 parsedOps2 -> "<IF_RIGHT: "+|parsedOps1|+", "+|parsedOps2|+">"++instance NFData Macro expandList :: [ParsedOp] -> [ExpandedOp] expandList = fmap (expand [])
src/Michelson/OpSize.hs view
@@ -77,7 +77,7 @@ EMPTY_MAP ta va ct t -> annsOpSize ta va <> comparableOpSize ct <> typeOpSize t EMPTY_BIG_MAP ta va ct t -> annsOpSize ta va <> comparableOpSize ct <> typeOpSize t MAP va is -> annsOpSize va <> subcodeOpSize is- ITER is -> annsOpSize <> subcodeOpSize is+ ITER is -> subcodeOpSize is MEM va -> annsOpSize va GET va -> annsOpSize va UPDATE va -> annsOpSize va@@ -216,26 +216,28 @@ ctOpSize :: CT -> OpSize ctOpSize _ = OpSize 2 --- | Accepts arbitrary number of 'Annotation's (maybe of different types)--- which belong to the same entity and returns their total operation size.+-- | Accepts an arbitrary number of 'TypeAnn' 'FieldAnn' and/or 'VarAnn' that+-- belong to the same entity and returns their total operation size. -- -- Note that annotations which belong to the same entity (type or instruction)--- __must be__ considered in aggregate using one call of this function.+-- __must be__ considered in aggregate using one call of this function and to be+-- specified in order. See 'AnnotationSet' for details. annsOpSize :: AnnsOpSizeVararg x => x-annsOpSize = annsOpSizeVararg []+annsOpSize = annsOpSizeVararg emptyAnnSet class AnnsOpSizeVararg x where- annsOpSizeVararg :: [SomeAnn] -> x+ annsOpSizeVararg :: AnnotationSet -> x -instance AnnsOpSizeVararg x => AnnsOpSizeVararg (Annotation t -> x) where- annsOpSizeVararg acc an = annsOpSizeVararg (convAnn an : acc)-instance AnnsOpSizeVararg x => AnnsOpSizeVararg ([Annotation t] -> x) where- annsOpSizeVararg acc an = annsOpSizeVararg (map convAnn an <> acc)+instance (KnownAnnTag t, AnnsOpSizeVararg x) => AnnsOpSizeVararg (Annotation t -> x) where+ annsOpSizeVararg acc an = annsOpSizeVararg (acc <> singleAnnSet an) +instance (KnownAnnTag t, AnnsOpSizeVararg x) => AnnsOpSizeVararg ([Annotation t] -> x) where+ annsOpSizeVararg acc ans = annsOpSizeVararg (acc <> singleGroupAnnSet ans)+ instance AnnsOpSizeVararg OpSize where annsOpSizeVararg = annsOpSizeImpl -annsOpSizeImpl :: [SomeAnn] -> OpSize-annsOpSizeImpl anns = case filter (/= noAnn) anns of- [] -> mempty- as -> OpSize . fromIntegral $ 3 * (length as + 1)+annsOpSizeImpl :: AnnotationSet -> OpSize+annsOpSizeImpl annSet+ | isNoAnnSet annSet = mempty+ | otherwise = OpSize . fromIntegral $ 3 * (minAnnSetSize annSet + 1)
src/Michelson/Optimizer.hs view
@@ -21,12 +21,14 @@ import Prelude hiding (EQ) import Data.Default (Default(def))+import Data.Singletons (sing) import Michelson.Interpret.Pack (packValue') import Michelson.Typed.Aliases (Value) import Michelson.Typed.CValue import Michelson.Typed.Instr import Michelson.Typed.Scope (PackedValScope)+import Michelson.Typed.Sing import Michelson.Typed.T import Michelson.Typed.Util (DfsSettings(..), dfsInstr) import Michelson.Typed.Value@@ -89,6 +91,9 @@ `orSimpleRule` simpleDrops `orSimpleRule` internalNop `orSimpleRule` simpleDips+ `orSimpleRule` adjacentDips+ `orSimpleRule` specificPush+ `orSimpleRule` pairUnpair -- | We do not enable 'pushPack' rule by default because it is -- potentially dangerous.@@ -129,10 +134,19 @@ Seq (PUSH _) (Seq DROP c) -> Just c Seq DUP (Seq DROP c) -> Just c Seq UNIT (Seq DROP c) -> Just c+ Seq NOW (Seq DROP c) -> Just c+ Seq SENDER (Seq DROP c) -> Just c+ Seq EMPTY_MAP (Seq DROP c) -> Just c+ Seq EMPTY_SET (Seq DROP c) -> Just c+ Seq SWAP SWAP -> Just Nop Seq (PUSH _) DROP -> Just Nop Seq DUP DROP -> Just Nop Seq UNIT DROP -> Just Nop+ Seq NOW DROP -> Just Nop+ Seq SENDER DROP -> Just Nop+ Seq EMPTY_MAP DROP -> Just Nop+ Seq EMPTY_SET DROP -> Just Nop _ -> Nothing dupSwap2dup :: Rule@@ -149,6 +163,14 @@ Seq (PUSH x) (DIP f) -> Just (Seq f (PUSH x)) Seq UNIT (Seq (DIP f) c) -> Just (Seq f (Seq UNIT c)) Seq UNIT (DIP f) -> Just (Seq f UNIT)+ Seq NOW (Seq (DIP f) c) -> Just (Seq f (Seq NOW c))+ Seq NOW (DIP f) -> Just (Seq f NOW)+ Seq SENDER (Seq (DIP f) c) -> Just (Seq f (Seq SENDER c))+ Seq SENDER (DIP f) -> Just (Seq f SENDER)+ Seq EMPTY_MAP (Seq (DIP f) c) -> Just (Seq f (Seq EMPTY_MAP c))+ Seq EMPTY_MAP (DIP f) -> Just (Seq f EMPTY_MAP)+ Seq EMPTY_SET (Seq (DIP f) c) -> Just (Seq f (Seq EMPTY_SET c))+ Seq EMPTY_SET (DIP f) -> Just (Seq f EMPTY_SET) -- If we do something ignoring top of the stack and then immediately -- drop top of the stack, we can drop that item in advance and@@ -233,6 +255,48 @@ -- @gromak: same situation as with `DROP 1` (see above). -- Seq (DIPN (SS SZ) i) c -> Just (Seq (DIP i) c) -- DIPN (SS SZ) i -> Just (DIP i)++ _ -> Nothing++adjacentDips :: Rule+adjacentDips = \case+ Seq (DIP f) (DIP g) -> Just (DIP (Seq f g))++ _ -> Nothing++specificPush :: Rule+specificPush = \case+ push@PUSH{} -> optimizePush push+ Seq (push@PUSH{}) c -> (`Seq` c) <$> optimizePush push++ _ -> Nothing+ where+ optimizePush :: Instr inp out -> Maybe (Instr inp out)+ optimizePush = \case+ PUSH v | _ :: Value v <- v -> case v of+ VUnit -> Just UNIT+ VMap m+ | null m -> case sing @v of STMap{} -> Just EMPTY_MAP+ VSet m+ | null m -> case sing @v of STSet{} -> Just EMPTY_SET+ _ -> Nothing++ _ -> Nothing++-- UNPAIR with continuation+pattern UNPAIR_c+ :: ()+ => (i ~ ('TPair a b : s), i' ~ (a : b : s), o' ~ o)+ => Instr i' o' -> Instr i o+pattern UNPAIR_c c = Seq DUP (Seq CAR (Seq (DIP CDR) c))++pairUnpair :: Rule+pairUnpair = \case+ Seq PAIR (UNPAIR_c c) -> Just c+ Seq PAIR UNPAIR -> Just Nop++ UNPAIR_c (Seq PAIR c) -> Just c+ UNPAIR_c PAIR -> Just Nop _ -> Nothing
src/Michelson/Parser.hs view
@@ -199,6 +199,7 @@ primOrMac = (macWithPos (ifCmpMac parsedOp) <|> ifOrIfX) <|> (macWithPos (mapCadrMac parsedOp) <|> primWithPos (mapOp parsedOp)) <|> (try (primWithPos pairOp) <|> macWithPos pairMac)+ <|> (try (macWithPos duupMac) <|> try (macWithPos dupNMac) <|> primWithPos dupOp) ------------------------------------------------------------------------------- -- Safe construction of Haskell values
src/Michelson/Parser/Error.hs view
@@ -24,8 +24,10 @@ | WrongAccessArgs Natural Positive | WrongSetArgs Natural Positive | ExcessFieldAnnotation- deriving stock (Eq, Data, Ord, Show)+ deriving stock (Eq, Data, Ord, Show, Generic) +instance NFData CustomParserException+ instance ShowErrorComponent CustomParserException where showErrorComponent UnknownTypeException = "unknown type" showErrorComponent (StringLiteralException e) = showErrorComponent e@@ -44,7 +46,9 @@ data StringLiteralParserException = InvalidEscapeSequence Char | InvalidChar Char- deriving stock (Eq, Data, Ord, Show)+ deriving stock (Eq, Data, Ord, Show, Generic)++instance NFData StringLiteralParserException instance ShowErrorComponent StringLiteralParserException where showErrorComponent (InvalidEscapeSequence c) =
src/Michelson/Parser/Instr.hs view
@@ -7,6 +7,7 @@ , mapOp , pairOp , cmpOp+ , dupOp ) where import Prelude hiding (EQ, GT, LT, many, note, some, try)@@ -26,7 +27,7 @@ -- | Parser for primitive Michelson instruction (no macros and extensions). primInstr :: Parser (Contract' ParsedOp) -> Parser ParsedOp -> Parser ParsedInstr primInstr contractParser opParser = choice- [ dropOp, dupOp, swapOp, digOp, dugOp, pushOp opParser, someOp, noneOp, unitOp+ [ dropOp, swapOp, digOp, dugOp, pushOp opParser, someOp, noneOp, unitOp , ifNoneOp opParser, carOp, cdrOp, leftOp, rightOp, ifLeftOp opParser, nilOp , consOp, ifConsOp opParser, sizeOp, emptySetOp, emptyMapOp, emptyBigMapOp, iterOp opParser , memOp, getOp, updateOp, loopLOp opParser, loopOp opParser@@ -42,7 +43,7 @@ -- | Parse a sequence of instructions. ops' :: Parser ParsedOp -> Parser [ParsedOp]-ops' opParser = (braces $ sepEndBy opParser semicolon) <|> (pure <$> opParser)+ops' opParser = (braces $ sepEndBy opParser (optional semicolon)) <|> (pure <$> opParser) -- Control Structures
src/Michelson/Parser/Macro.hs view
@@ -3,6 +3,8 @@ module Michelson.Parser.Macro ( macro -- * These are handled separately to have better error messages+ , dupNMac+ , duupMac , pairMac , ifCmpMac , mapCadrMac@@ -47,13 +49,20 @@ <|> word' "ASSERT_" ASSERTX <*> cmpOp <|> word' "ASSERT" ASSERT <|> do string' "DI"; n <- num "I"; symbol' "P"; DIIP (n + 1) <$> ops- <|> do string' "DU"; n <- num "U"; symbol' "P"; DUUP (n + 1) <$> noteDef <|> unpairMac <|> cadrMac <|> setCadrMac where ops = ops' opParser num str = fromIntegral . length <$> some (string' str)++dupNMac :: Parser Macro+dupNMac = do symbol' "DUP"; DUUP <$> lexeme decimal <*> noteDef++duupMac :: Parser Macro+duupMac = do string' "DU"; n <- num "U"; symbol' "P"; DUUP (n + 1) <$> noteDef+ where+ num str = fromIntegral . length <$> some (string' str) pairMac :: Parser Macro pairMac = do
src/Michelson/Parser/Types.hs view
@@ -10,9 +10,9 @@ import qualified Data.Map as Map import Text.Megaparsec (Parsec) -import Michelson.Parser.Error import Michelson.Let (LetType, LetValue) import Michelson.Macro (LetMacro)+import Michelson.Parser.Error type Parser = ReaderT LetEnv (Parsec CustomParserException Text)
src/Michelson/Printer.hs view
@@ -2,7 +2,7 @@ ( RenderDoc(..) , printDoc , printUntypedContract- , printTypedContract+ , printTypedContractCode , printTypedFullContract , printSomeContract , printTypedValue@@ -24,9 +24,9 @@ -- | Convert a typed contract into a textual representation which -- will be accepted by the OCaml reference client.-printTypedContract :: (SingI p, SingI s) => Bool -> T.Contract p s -> TL.Text-printTypedContract forceSingleLine =- printUntypedContract forceSingleLine . T.convertContract+printTypedContractCode :: (SingI p, SingI s) => Bool -> T.ContractCode p s -> TL.Text+printTypedContractCode forceSingleLine =+ printUntypedContract forceSingleLine . T.convertContractCode printTypedFullContract :: Bool -> T.FullContract p s -> TL.Text printTypedFullContract forceSingleLine fc@T.FullContract{} =
src/Michelson/Printer/Util.hs view
@@ -3,6 +3,7 @@ , Prettier(..) , printDoc , printDocB+ , printDocS , renderOps , renderOpsList , spaces@@ -61,6 +62,10 @@ -- | Convert 'Doc' to 'Builder' in the same maner as 'printDoc'. printDocB :: Bool -> Doc -> Builder printDocB oneLine = displayB . doRender oneLine++-- | Convert 'Doc' to 'String' in the same maner as 'printDoc'.+printDocS :: Bool -> Doc -> String+printDocS oneLine = toString . printDoc oneLine -- | Generic way to render the different op types that get passed -- to a contract.
src/Michelson/Runtime.hs view
@@ -33,7 +33,6 @@ import Control.Lens (at, makeLenses, (%=)) import Control.Monad.Except (Except, runExcept, throwError)-import Data.Singletons (sing) import Data.Text.IO (getContents) import Fmt (Buildable(build), blockListF, fmt, fmtLn, nameF, pretty, (+|), (|+)) import Named ((:!), (:?), arg, argDef, defaults, (!))@@ -49,8 +48,8 @@ import Michelson.TypeCheck (SomeContract, TCError, typeCheckContract, typeCheckTopLevelType, typeVerifyTopLevelType) import Michelson.Typed- (CreateContract(..), EpName, Operation'(..), SomeValue'(..), TransferTokens(..), convertContract,- untypeValue)+ (CreateContract(..), EpName, Operation'(..), SomeValue'(..), TransferTokens(..),+ convertContractCode, untypeValue) import qualified Michelson.Typed as T import Michelson.Untyped (Contract, OriginationOperation(..), mkContractAddress) import qualified Michelson.Untyped as U@@ -445,10 +444,13 @@ (Just (ASContract cs), _) -> do let existingContracts = extractAllContracts gs+ -- can't overflow if global state is correct (because we can't+ -- create money out of nowhere)+ newBalance = csBalance cs `unsafeAddMutez` tdAmount txData contractEnv = ContractEnv { ceNow = now , ceMaxSteps = remainingSteps- , ceBalance = csBalance cs+ , ceBalance = newBalance , ceContracts = existingContracts , ceSelf = addr , ceSource = sourceAddr@@ -461,7 +463,7 @@ SomeContractAndStorage typedContract typedStorage <- getTypedContractAndStorage EEIllTypedContract EEIllTypedStorage gs cs T.MkEntryPointCallRes _ epc- <- T.mkEntryPointCall epName (sing, T.fcParamNotesSafe typedContract)+ <- T.mkEntryPointCall epName (T.fcParamNotesSafe typedContract) & maybe (throwError $ EEUnknownEntrypoint epName) pure typedParameter <- first EEIllTypedParameter $ typeVerifyTopLevelType existingContracts (tdParameter txData)@@ -476,9 +478,6 @@ typedParameter typedStorage contractEnv let newValueU = untypeValue newValue- -- can't overflow if global state is correct (because we can't- -- create money out of nowhere)- newBalance = csBalance cs `unsafeAddMutez` tdAmount txData updBalance | newBalance == csBalance cs = Nothing | otherwise = Just $ GSSetBalance addr newBalance@@ -545,6 +544,6 @@ , ooDelegate = ccDelegate cc , ooBalance = ccBalance cc , ooStorage = untypeValue (ccStorageVal cc)- , ooContract = convertContract (ccContractCode cc)+ , ooContract = convertContractCode (ccContractCode cc) } in Just (OriginateOp origination)
src/Michelson/Test.hs view
@@ -19,7 +19,9 @@ , ContractPropValidator , contractProp , contractPropVal+ , validateSuccess , validateStorageIs+ , validateMichelsonFailsWith -- * Integrational testing -- ** Testing engine
src/Michelson/Test/Import.hs view
@@ -32,7 +32,7 @@ import Michelson.Parser.Error (ParserException(..)) import Michelson.Runtime (parseExpandContract, prepareContract) import Michelson.TypeCheck (SomeContract(..), TCError, typeCheckContract)-import Michelson.Typed (Contract, FullContract(..), toUType)+import Michelson.Typed (FullContract(..), toUType) import qualified Michelson.Untyped as U import Util.IO @@ -48,7 +48,7 @@ -- result will notify about problem). testTreesWithContract :: (Each [Typeable, SingI] [cp, st], HasCallStack)- => FilePath -> ((U.Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree]+ => FilePath -> ((U.Contract, FullContract cp st) -> IO [TestTree]) -> IO [TestTree] testTreesWithContract = testTreesWithContractImpl importContract -- | Like 'testTreesWithContract' but supplies only untyped contract.@@ -61,7 +61,7 @@ -- | Like 'testTreesWithContract' but supplies only typed contract. testTreesWithTypedContract :: (Each [Typeable, SingI] [cp, st], HasCallStack)- => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree]+ => FilePath -> (FullContract cp st -> IO [TestTree]) -> IO [TestTree] testTreesWithTypedContract = testTreesWithContractImpl (fmap snd . importContract) @@ -94,14 +94,14 @@ -- result will notify about problem). specWithContract :: (Each [Typeable, SingI] [cp, st], HasCallStack)- => FilePath -> ((U.Contract, Contract cp st) -> Spec) -> Spec+ => FilePath -> ((U.Contract, FullContract cp st) -> Spec) -> Spec specWithContract = specWithContractImpl importContract -- | A version of 'specWithContract' which passes only the typed -- representation of the contract. specWithTypedContract :: (Each [Typeable, SingI] [cp, st], HasCallStack)- => FilePath -> (Contract cp st -> Spec) -> Spec+ => FilePath -> (FullContract cp st -> Spec) -> Spec specWithTypedContract = specWithContractImpl (fmap snd . importContract) specWithUntypedContract :: FilePath -> (U.Contract -> Spec) -> Spec@@ -137,13 +137,13 @@ Each [Typeable, SingI] [cp, st] => FilePath -> Text- -> Either ImportContractError (U.Contract, Contract cp st)+ -> Either ImportContractError (U.Contract, FullContract cp st) readContract filePath txt = do contract <- first ICEParse $ parseExpandContract (Just filePath) txt- SomeContract (FullContract (instr :: Contract cp' st') _ _)+ SomeContract (tContract@FullContract{} :: FullContract cp' st') <- first ICETypeCheck $ typeCheckContract mempty contract case (eqT @cp @cp', eqT @st @st') of- (Just Refl, Just Refl) -> pure (contract, instr)+ (Just Refl, Just Refl) -> pure (contract, tContract) (Nothing, _) -> Left $ ICEUnexpectedParamType (U.para contract) (toUType $ demote @cp) _ -> Left (ICEUnexpectedStorageType (U.stor contract) (toUType $ demote @st))@@ -158,7 +158,7 @@ importContract :: forall cp st . Each [Typeable, SingI] [cp, st]- => FilePath -> IO (U.Contract, Contract cp st)+ => FilePath -> IO (U.Contract, FullContract cp st) importContract file = either throwM pure =<< readContract file <$> readFileUtf8 file importUntypedContract :: FilePath -> IO U.Contract
src/Michelson/Test/Unit.hs view
@@ -7,19 +7,24 @@ , contractPropVal , contractHasEntryPoints , matchContractEntryPoints+ , mkEntrypointsMap , hasEp+ , validateSuccess , validateStorageIs+ , validateMichelsonFailsWith ) where import Data.List.NonEmpty (fromList) import qualified Data.Map as Map+import Data.Singletons (SingI) import Fmt ((+|), (|+))+import Test.Hspec.Expectations (Expectation, shouldBe, shouldSatisfy) import Test.HUnit (Assertion, assertFailure, (@?=)) -import Michelson.Interpret (ContractEnv, ContractReturn, interpret)+import Michelson.Interpret (ContractEnv, ContractReturn, MichelsonFailed(..), interpret) import Michelson.Printer (printUntypedContract) import Michelson.Runtime (parseExpandContract)-import Michelson.Typed (Contract, IsoValue(..), ToT, epNameToParamAnn)+import Michelson.Typed (FullContract, IsoValue(..), ToT, epNameToParamAnn) import qualified Michelson.Typed as T import Michelson.Untyped (EpName, para) import Michelson.Untyped hiding (Contract)@@ -35,7 +40,7 @@ -- or anything else relevant. type ContractPropValidator st prop = ContractReturn st -> prop --- | Contract's property tester against given input.+-- | ContractCode's property tester against given input. -- Takes contract environment, initial storage and parameter, -- interprets contract on this input and invokes validation function. contractProp@@ -43,7 +48,7 @@ , ToT param ~ cp, ToT storage ~ st , T.ParameterScope cp )- => Contract cp st+ => FullContract cp st -> ContractPropValidator st prop -> ContractEnv -> param@@ -61,14 +66,14 @@ -- by "Michelson.Test.Integrational" module. contractPropVal :: (T.ParameterScope cp)- => Contract cp st+ => FullContract cp st -> ContractPropValidator st prop -> ContractEnv -> T.Value cp -> T.Value st -> prop contractPropVal instr check env param initSt =- check $ interpret instr T.epcCallRootUnsafe param initSt env+ check $ interpret (T.fcCode instr) T.epcCallRootUnsafe param initSt env -- | Check if entrypoint is present in `T`. hasEp :: T -> (EpName, U.Type) -> Bool@@ -100,6 +105,14 @@ conv l | null l = Right () | otherwise = Left $ fromList l +----------------------------------------------------------------------------+-- Validators+----------------------------------------------------------------------------++-- | 'ContractPropValidator' that expects a successful termination.+validateSuccess :: HasCallStack => ContractPropValidator st Expectation+validateSuccess (res, _) = res `shouldSatisfy` isRight+ -- | 'ContractPropValidator' that expects contract execution to -- succeed and update storage to a particular constant value. validateStorageIs@@ -111,3 +124,9 @@ assertFailure $ "Unexpected interpretation failure: " +| err |+ "" Right (_ops, got) -> got @?= toVal expected++-- | 'ContractPropValidator' that expects a given failure.+validateMichelsonFailsWith+ :: (T.IsoValue v, Typeable (ToT v), SingI (ToT v))+ => v -> ContractPropValidator st Expectation+validateMichelsonFailsWith v (res, _) = res `shouldBe` Left (MichelsonFailedWith $ toVal v)
src/Michelson/Text.hs view
@@ -35,13 +35,14 @@ import qualified Data.Char as C import Data.Data (Data) import qualified Data.Text as T-import Data.Vinyl.Derived (Label) import Fmt (Buildable) import GHC.TypeLits (ErrorMessage(..), TypeError) import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Quote as TH import Test.QuickCheck (Arbitrary(..), choose, listOf) +import Util.CLI+import Util.Label (Label(..), labelToText) import Util.TypeLits -- | Michelson string value.@@ -66,9 +67,11 @@ -- * With 'mkMTextCut' when not sure about text contents and want -- to make it compliant with Michelson constraints. newtype MText = MTextUnsafe { unMText :: Text }- deriving stock (Show, Eq, Ord, Data)+ deriving stock (Show, Eq, Ord, Data, Generic) deriving newtype (Semigroup, Monoid, Container, Buildable, Hashable) +instance NFData MText+ -- | Constraint on literals appearing in Michelson contract code. isMChar :: Char -> Bool isMChar c = fromEnum c >= 32 && fromEnum c <= 126@@ -129,6 +132,10 @@ parseJSON v = either (fail . toString) pure . mkMText =<< parseJSON @Text v +instance HasCLReader MText where+ getReader = eitherReader (first toString . mkMText . toText)+ getMetavar = "MICHELSON STRING"+ -- | QuasyQuoter for constructing Michelson strings. -- -- Validity of result will be checked at compile time.@@ -201,8 +208,8 @@ -- -- We assume that no unicode characters are used in plain Haskell code, -- so unless special tricky manipulations are used this should be safe.-labelToMText :: KnownSymbol name => Label name -> MText-labelToMText (_ :: Label name) = symbolToMText @name+labelToMText :: Label name -> MText+labelToMText = mkMTextUnsafe . labelToText -- | Leads first character of text to upper case. --
src/Michelson/TypeCheck.hs view
@@ -3,7 +3,6 @@ , typeCheckTopLevelType , typeVerifyTopLevelType , typeCheckValue- , typeVerifyValue , typeCheckList , typeCheckCValue , typeCheckExt
src/Michelson/TypeCheck/Error.hs view
@@ -29,8 +29,10 @@ | NotEnoughDip | NotEnoughDig | NotEnoughDug- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData NotEnoughItemsInstr+ instance Buildable NotEnoughItemsInstr where build = \case NotEnoughDrop -> "DROP"@@ -76,8 +78,10 @@ -- ^ KeyHash couldn't be parsed from its textual representation | InvalidTimestamp -- ^ Timestamp is not RFC339 compliant- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData TCTypeError+ instance Buildable TCTypeError where build = \case AnnError e -> build e@@ -110,8 +114,10 @@ | TCContractError Text (Maybe TCTypeError) | TCUnreachableCode InstrCallStack (NonEmpty U.ExpandedOp) | TCExtError SomeHST InstrCallStack ExtError- deriving stock (Eq)+ deriving stock (Eq, Generic) +instance NFData TCError+ -- TODO pva701: an instruction position should be used in -- Buildable instance within TM-151. instance Buildable TCError where@@ -145,8 +151,10 @@ instance Buildable U.ExpandedInstr => Exception TCError newtype StackSize = StackSize Natural- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData StackSize+ -- | Various type errors possible when checking Morley extension commands data ExtError = LengthMismatch U.StackTypePattern@@ -156,7 +164,9 @@ | StkRestMismatch U.StackTypePattern SomeHST SomeHST TCTypeError | TestAssertError Text | InvalidStackReference U.StackRef StackSize- deriving stock (Eq)+ deriving stock (Eq, Generic)++instance NFData ExtError instance Buildable ExtError where build = \case
src/Michelson/TypeCheck/Ext.hs view
@@ -6,7 +6,6 @@ import Control.Lens ((%=)) import Control.Monad.Except (MonadError, liftEither, throwError) import Data.Map.Lazy (Map, insert, lookup)-import Data.Singletons (Sing) import Data.Typeable ((:~:)(..)) import Michelson.ErrorPos@@ -14,9 +13,8 @@ import Michelson.TypeCheck.Helpers import Michelson.TypeCheck.TypeCheck import Michelson.TypeCheck.Types-import Michelson.Typed (Notes(..), converge, mkUType, withUType)+import Michelson.Typed (Notes(..), converge, mkUType, notesT, withUType) import qualified Michelson.Typed as T-import Michelson.Typed.Sing (fromSingT) import Michelson.Untyped (CT(..), ExpandedOp, StackFn, TyVar(..), Type, Var, VarAnn, inPattern, outPattern, quantifiedVars, varSet)@@ -47,7 +45,7 @@ case si of AnyOutInstr _ -> throwError $ TCExtError (SomeHST hst) instrPos $ TestAssertError "TEST_ASSERT has to return Bool, but it always fails"- instr ::: (((_ :: (Sing b, T.Notes b, VarAnn)) ::& (_ :: HST out1))) -> do+ instr ::: (((_ :: (T.Notes b, VarAnn)) ::& (_ :: HST out1))) -> do Refl <- liftEither $ first (const $ TCExtError (SomeHST hst) instrPos $ TestAssertError "TEST_ASSERT has to return Bool, but returned something else") $@@ -127,20 +125,20 @@ go m _ U.StkEmpty SNil = pure $ BoundVars m Nothing go _ _ U.StkEmpty _ = Left $ LengthMismatch s go _ _ _ SNil = Left $ LengthMismatch s- go m n (U.StkCons tyVar ts) ((xt :: Sing xt, xann :: Notes xt, _) ::& xs) =+ go m n (U.StkCons tyVar ts) ((xann :: Notes xt, _) ::& xs) = let handleType :: U.Type -> Either ExtError BoundVars handleType t =- withUType t $ \(tsing :: Sing t, tann :: Notes t) -> do+ withUType t $ \(tann :: Notes t) -> do Refl <- first- (\_ -> TypeMismatch s n $ TypeEqError (fromSingT xt) (fromSingT tsing))+ (\_ -> TypeMismatch s n $ TypeEqError (notesT xann) (notesT tann)) (eqType @xt @t) void $ first (TypeMismatch s n . AnnError) (converge tann xann) go m (n + 1) ts xs in case tyVar of TyCon t -> handleType t VarID v -> case lookup v m of- Nothing -> let t = mkUType xt xann in go (insert v t m) (n + 1) ts xs+ Nothing -> let t = mkUType xann in go (insert v t m) (n + 1) ts xs Just t -> handleType t -- | Create stack reference accessing element with a given index.
src/Michelson/TypeCheck/Helpers.hs view
@@ -52,7 +52,7 @@ import Michelson.TypeCheck.TypeCheck import Michelson.TypeCheck.Types import Michelson.Typed- (CT(..), Instr(..), Notes(..), PackedNotes(..), Sing(..), T(..), converge, fromSingT, orAnn,+ (CT(..), Instr(..), Notes(..), PackedNotes(..), Sing(..), T(..), converge, notesT, orAnn, starNotes) import Michelson.Typed.Annotation (AnnConvergeError, isStar) import Michelson.Typed.Arith (Add, ArithOp(..), Mul, Sub, UnaryArithOp(..))@@ -123,12 +123,12 @@ in (an, avn) convergeHSTEl- :: (Sing t, Notes t, VarAnn)- -> (Sing t, Notes t, VarAnn)- -> Either AnnConvergeError (Sing t, Notes t, VarAnn)-convergeHSTEl (at, an, avn) (_, bn, bvn) =- (,,) at <$> converge an bn- <*> pure (bool def avn $ avn == bvn)+ :: (Notes t, VarAnn)+ -> (Notes t, VarAnn)+ -> Either AnnConvergeError (Notes t, VarAnn)+convergeHSTEl (an, avn) (bn, bvn) =+ (,) <$> converge an bn+ <*> pure (bool def avn $ avn == bvn) -- | Combine annotations from two given stack types convergeHST :: HST ts -> HST ts -> Either AnnConvergeError (HST ts)@@ -144,7 +144,7 @@ hstToTs :: HST st -> [T] hstToTs = \case SNil -> []- (s, _, _) ::& hst -> fromSingT s : hstToTs hst+ (notes, _) ::& hst -> notesT notes : hstToTs hst -- | Check whether the given stack types are equal. eqHST@@ -304,8 +304,8 @@ wrapWithNotes :: HST d -> Instr c d -> Instr c d wrapWithNotes h ins = case h of -- do not wrap in notes if the notes are "star"- ((_, n, _) ::& _) | isStar n -> ins- ((s, n, _) ::& _) -> InstrWithNotes (PackedNotes n s) ins+ ((n, _) ::& _) | isStar n -> ins+ ((n, _) ::& _) -> InstrWithNotes (PackedNotes n) ins SNil -> ins extractInstrPos :: Un.ExpandedOp -> InstrCallStack@@ -340,7 +340,7 @@ memImpl instr i@(_ ::& _ ::& rs) vn = do pos <- ask case eqType @('Tc q) @('Tc (MemOpKey c)) of- Right Refl -> pure $ i :/ MEM ::: ((STc SCBool, starNotes, vn) ::& rs)+ Right Refl -> pure $ i :/ MEM ::: ((starNotes, vn) ::& rs) Left m -> throwError $ TCFailedOnInstr instr (SomeHST i) "query element type is not equal to set's element type" pos (Just m) @@ -355,16 +355,15 @@ ) => Un.ExpandedInstr -> HST (getKey ': c ': rs)- -> Sing (GetOpVal c) -> Notes (GetOpVal c) -> VarAnn -> m (SomeInstr inp)-getImpl instr i@(_ ::& _ ::& rs) rt vns vn = do+getImpl instr i@(_ ::& _ ::& rs) vns vn = do pos <- ask case eqType @getKey @('Tc (GetOpKey c)) of Right Refl -> do let rn = NTOption def vns- pure $ i :/ GET ::: ((STOption rt, rn, vn) ::& rs)+ pure $ i :/ GET ::: ((rn, vn) ::& rs) Left m -> throwError $ TCFailedOnInstr instr (SomeHST i) "wrong key stack type" pos (Just m) updImpl@@ -384,7 +383,7 @@ pos <- ask case (eqType @updKey @('Tc (UpdOpKey c)), eqType @updParams @(UpdOpParams c)) of (Right Refl, Right Refl) ->- pure $ i :/ UPDATE ::: ((cTuple & _3 .~ vn) ::& rest)+ pure $ i :/ UPDATE ::: ((cTuple & _2 .~ vn) ::& rest) (Left m, _) -> throwError $ TCFailedOnInstr instr (SomeHST i) "wrong key stack type" pos (Just m) (_, Left m) -> throwError $ TCFailedOnInstr instr (SomeHST i)@@ -396,25 +395,25 @@ -> VarAnn -> m (SomeInstr inp) sizeImpl i@(_ ::& rs) vn =- pure $ i :/ SIZE ::: ((STc SCNat, starNotes, vn) ::& rs)+ pure $ i :/ SIZE ::: ((starNotes, vn) ::& rs) sliceImpl :: (SliceOp c, Typeable c, inp ~ ('Tc 'CNat ': 'Tc 'CNat ': c ': rs), Monad m) => HST inp -> Un.VarAnn -> m (SomeInstr inp)-sliceImpl i@(_ ::& _ ::& (c, cn, cvn) ::& rs) vn = do+sliceImpl i@(_ ::& _ ::& (cn, cvn) ::& rs) vn = do let vn' = vn `orAnn` deriveVN "slice" cvn rn = NTOption def cn- pure $ i :/ SLICE ::: ((STOption c, rn, vn') ::& rs)+ pure $ i :/ SLICE ::: ((rn, vn') ::& rs) concatImpl'- :: (ConcatOp c, Typeable c, inp ~ ('TList c : rs), Monad m)+ :: (ConcatOp c, Typeable c, SingI c, inp ~ ('TList c : rs), Monad m) => HST inp -> Un.VarAnn -> m (SomeInstr inp)-concatImpl' i@((STList c, NTList _ n, _) ::& rs) vn = do- pure $ i :/ CONCAT' ::: ((c, n, vn) ::& rs)+concatImpl' i@((NTList _ n, _) ::& rs) vn = do+ pure $ i :/ CONCAT' ::: ((n, vn) ::& rs) concatImpl :: ( ConcatOp c, Typeable c, inp ~ (c ': c ': rs)@@ -424,9 +423,9 @@ => HST inp -> Un.VarAnn -> m (SomeInstr inp)-concatImpl i@((c, cn1, _) ::& (_, cn2, _) ::& rs) vn = do+concatImpl i@((cn1, _) ::& (cn2, _) ::& rs) vn = do cn <- onTypeCheckInstrAnnErr (Un.CONCAT vn) i "wrong operand types for concat operation" (converge cn1 cn2)- pure $ i :/ CONCAT ::: ((c, cn, vn) ::& rs)+ pure $ i :/ CONCAT ::: ((cn, vn) ::& rs) -- | Helper function to construct instructions for binary arithmetic -- operations.@@ -442,7 +441,7 @@ -> VarAnn -> t (SomeInstr inp) arithImpl mkInstr i@(_ ::& _ ::& rs) vn = do- pure $ i :/ mkInstr ::: ((sing, starNotes, vn) ::& rs)+ pure $ i :/ mkInstr ::: ((starNotes, vn) ::& rs) addImpl :: forall a b inp rs m.@@ -504,7 +503,7 @@ -> VarAnn -> t (SomeInstr inp) edivImplDo i@(_ ::& _ ::& rs) vn = do- pure $ i :/ EDIV ::: ((sing, starNotes, vn) ::& rs)+ pure $ i :/ EDIV ::: ((starNotes, vn) ::& rs) subImpl :: forall a b inp rs m.@@ -567,4 +566,4 @@ -> VarAnn -> t (SomeInstr inp) unaryArithImpl mkInstr i@(_ ::& rs) vn = do- pure $ i :/ mkInstr ::: ((sing, starNotes, vn) ::& rs)+ pure $ i :/ mkInstr ::: ((starNotes, vn) ::& rs)
src/Michelson/TypeCheck/Instr.hs view
@@ -27,7 +27,6 @@ module Michelson.TypeCheck.Instr ( typeCheckContract , typeCheckValue- , typeVerifyValue , typeCheckList , typeVerifyTopLevelType , typeCheckTopLevelType@@ -40,7 +39,7 @@ import Data.Default (def) import Data.Generics (everything, mkQ) import Data.Singletons (SingI(sing), demote)-import Data.Typeable ((:~:)(..), gcast)+import Data.Typeable ((:~:)(..)) import Michelson.ErrorPos import Michelson.TypeCheck.Error@@ -68,22 +67,22 @@ typeCheckContractImpl (U.Contract mParam mStorage pCode) = do code <- maybe (throwError $ TCContractError "no instructions in contract code" Nothing) pure (nonEmpty pCode)- withUType mParam $ \(paramS :: Sing param, paramNote :: Notes param) ->- withUType mStorage $ \(storageS :: Sing st, storageNote :: Notes st) -> do+ withUType mParam $ \(paramNote :: Notes param) ->+ withUType mStorage $ \(storageNote :: Notes st) -> do (Dict, Dict) <-- note (hasTypeError "parameter" paramS) $- (,) <$> opAbsense paramS- <*> nestedBigMapsAbsense paramS+ note (hasTypeError "parameter" paramNote) $+ (,) <$> opAbsense (notesSing paramNote)+ <*> nestedBigMapsAbsense (notesSing paramNote) (Dict, Dict, Dict) <-- note (hasTypeError "storage" storageS) $- (,,) <$> opAbsense storageS- <*> nestedBigMapsAbsense storageS- <*> contractTypeAbsense storageS+ note (hasTypeError "storage" storageNote) $+ (,,) <$> opAbsense (notesSing storageNote)+ <*> nestedBigMapsAbsense (notesSing storageNote)+ <*> contractTypeAbsense (notesSing storageNote) let inpNote = NTPair def def def paramNote storageNote- let inp = (STPair paramS storageS, inpNote, def) ::& SNil+ let inp = (inpNote, def) ::& SNil inp' :/ instrOut <- typeCheckNE code inp let (paramNotesRaw, fcStoreNotes) = case inp' of- (_, NTPair _ _ _ cpNotes stNotes, _) ::& SNil -> (cpNotes, stNotes)+ (NTPair _ _ _ cpNotes stNotes, _) ::& SNil -> (cpNotes, stNotes) fcParamNotesSafe <- liftEither $ mkParamNotes paramNotesRaw `onLeft`@@ -92,7 +91,7 @@ instr ::: out -> liftEither $ do case eqHST1 @(ContractOut1 st) out of Right Refl -> do- let (_, outN, _) ::& SNil = out+ let (outN, _) ::& SNil = out _ <- converge outN (NTPair def def def starNotes storageNote) `onLeft` ((TCContractError "contract output type violates convention:") . Just . AnnError)@@ -138,7 +137,7 @@ -- given in representation from @Michelson.Untyped@ module hierarchy to -- representation in strictly typed GADT. ----- As a second argument, @typeCheckValue@ accepts expected type of value.+-- @typeCheckValue@ is polymorphic in the expected type of value. -- -- Type checking algorithm pattern-matches on parse value representation, -- expected type @t@ and constructs @Val t@ value.@@ -147,26 +146,18 @@ -- that is interpreted as input of wrong type and type check finishes with -- error. typeCheckValue- :: U.Value- -> (Sing t, Notes t)- -> TypeCheckInstr SomeNotedValue-typeCheckValue = typeCheckValImpl typeCheckInstr---- | Like 'typeCheckValue', but returns value of a desired type.-typeVerifyValue :: forall t.- (Typeable t, SingI t)- => U.Value -> TypeCheckInstr (Value t)-typeVerifyValue uval =- typeCheckValue uval (sing @t, starNotes) <&> \case- val :::: _ -> gcast val ?: error "Typechecker produced value of wrong type"+ (SingI t)+ => U.Value+ -> TypeCheckInstr (Value t)+typeCheckValue = typeCheckValImpl @t typeCheckInstr typeVerifyTopLevelType- :: (Typeable t, SingI t, HasCallStack)+ :: (SingI t, HasCallStack) => TcOriginatedContracts -> U.Value -> Either TCError (Value t) typeVerifyTopLevelType originatedContracts valueU = runTypeCheck param originatedContracts $ usingReaderT (def :: InstrCallStack) $- typeVerifyValue valueU+ typeCheckValue valueU where param = error "parameter type touched during top-level type typecheck" @@ -282,169 +273,161 @@ (SomeHST i) "" (Left (NotEnoughItemsOnStack nTotal NotEnoughDug)) (U.PUSH vn mt mval, i) ->- withUType mt $ \(t' :: Sing t', nt' :: Notes t') -> do- val :::: (t :: Sing t, nt) <- typeCheckValue mval (t', nt')+ withUType mt $ \(nt :: Notes t) -> do+ val <- typeCheckValue @t mval proofOp <- maybe (onTypeCheckInstrErr uInstr (SomeHST i) "Operations in constant are not allowed" $ Left $ UnsupportedTypes [demote @t])- pure (opAbsense t)+ pure (opAbsense $ notesSing nt) proofBigMap <- maybe (onTypeCheckInstrErr uInstr (SomeHST i) "BigMaps in constant are not allowed" $ Left $ UnsupportedTypes [demote @t])- pure (bigMapAbsense t)+ pure (bigMapAbsense $ notesSing nt) proofContract <- maybe (onTypeCheckInstrErr uInstr (SomeHST i) "Contract type in constant is not allowed" $ Left $ UnsupportedTypes [demote @t])- pure (contractTypeAbsense t)+ pure (contractTypeAbsense $ notesSing nt) case (proofOp, proofBigMap, proofContract) of- (Dict, Dict, Dict) -> pure $ i :/ PUSH val ::: ((t, nt, vn) ::& i)+ (Dict, Dict, Dict) -> pure $ i :/ PUSH val ::: ((nt, vn) ::& i) - (U.SOME tn vn, (at, an, _) ::& rs) -> do- pure (inp :/ SOME ::: ((STOption at, NTOption tn an, vn) ::& rs))+ (U.SOME tn vn, (an, _) ::& rs) -> do+ pure (inp :/ SOME ::: ((NTOption tn an, vn) ::& rs)) (U.NONE tn vn elMt, _) ->- withUType elMt $ \(elSing :: Sing t, elNotes :: Notes t) ->- pure $ inp :/ NONE ::: ((STOption elSing, NTOption tn elNotes, vn) ::& inp)+ withUType elMt $ \elNotes ->+ pure $ inp :/ NONE ::: ((NTOption tn elNotes, vn) ::& inp) (U.UNIT tn vn, _) ->- pure $ inp :/ UNIT ::: ((STUnit, NTUnit tn, vn) ::& inp)+ pure $ inp :/ UNIT ::: ((NTUnit tn, vn) ::& inp) - (U.IF_NONE mp mq, (STOption a, ons, ovn) ::& rs) -> do+ (U.IF_NONE mp mq, (STOption{}, ons, ovn) ::&+ rs) -> do let (an, avn) = deriveNsOption ons ovn- genericIf IF_NONE U.IF_NONE mp mq rs ((a, an, avn) ::& rs) inp+ genericIf IF_NONE U.IF_NONE mp mq rs ((an, avn) ::& rs) inp - (U.PAIR tn vn pfn qfn, (a, an, avn) ::& (b, bn, bvn) ::& rs) -> do+ (U.PAIR tn vn pfn qfn, (an, avn) ::& (bn, bvn) ::& rs) -> do let (vn', pfn', qfn') = deriveSpecialFNs pfn qfn avn bvn ns = NTPair tn pfn' qfn' an bn- pure (inp :/ PAIR ::: ((STPair a b, ns, vn `orAnn` vn') ::& rs))+ pure (inp :/ PAIR ::: ((ns, vn `orAnn` vn') ::& rs)) - (U.CAR vn fn, ( STPair a b- , NTPair pairTN pfn qfn pns qns- , pairVN ) ::& rs) -> do+ (U.CAR vn fn, (STPair{}, NTPair pairTN pfn qfn pns qns, pairVN) ::&+ rs) -> do pfn' <- onTypeCheckInstrAnnErr uInstr inp "wrong car type:" (convergeAnns fn pfn) let vn' = deriveSpecialVN vn pfn' pairVN- i' = ( STPair a b- , NTPair pairTN pfn' qfn pns qns- , pairVN ) ::& rs- pure $ i' :/ AnnCAR fn ::: ((a, pns, vn') ::& rs)+ i' = (NTPair pairTN pfn' qfn pns qns, pairVN) ::& rs+ pure $ i' :/ AnnCAR fn ::: ((pns, vn') ::& rs) - (U.CDR vn fn, ( STPair a b- , NTPair pairTN pfn qfn pns qns- , pairVN ) ::& rs) -> do+ (U.CDR vn fn, (STPair{}, NTPair pairTN pfn qfn pns qns, pairVN) ::&+ rs) -> do qfn' <- onTypeCheckInstrAnnErr uInstr inp "wrong cdr type:" (convergeAnns fn qfn) let vn' = deriveSpecialVN vn qfn' pairVN- i' = ( STPair a b- , NTPair pairTN pfn qfn' pns qns- , pairVN ) ::& rs- pure $ i' :/ AnnCDR fn ::: ((b, qns, vn') ::& rs)+ i' = (NTPair pairTN pfn qfn' pns qns, pairVN) ::& rs+ pure $ i' :/ AnnCDR fn ::: ((qns, vn') ::& rs) - (U.LEFT tn vn pfn qfn bMt, (a, an, _) ::& rs) ->- withUType bMt $ \(b :: Sing b, bn :: Notes b) -> do+ (U.LEFT tn vn pfn qfn bMt, (an, _) ::& rs) ->+ withUType bMt $ \bn -> do let ns = NTOr tn pfn qfn an bn- pure (inp :/ LEFT ::: ((STOr a b, ns, vn) ::& rs))+ pure (inp :/ LEFT ::: ((ns, vn) ::& rs)) - (U.RIGHT tn vn pfn qfn aMt, (b, bn, _) ::& rs) ->- withUType aMt $ \(a :: Sing a, an :: Notes a) -> do+ (U.RIGHT tn vn pfn qfn aMt, (bn, _) ::& rs) ->+ withUType aMt $ \an -> do let ns = NTOr tn pfn qfn an bn- pure (inp :/ RIGHT ::: ((STOr a b, ns, vn) ::& rs))+ pure (inp :/ RIGHT ::: ((ns, vn) ::& rs)) - (U.IF_LEFT mp mq, (STOr a b, ons, ovn) ::& rs) -> do+ (U.IF_LEFT mp mq, (STOr{}, ons, ovn) ::&+ rs) -> do let (an, bn, avn, bvn) = deriveNsOr ons ovn- ait = (a, an, avn) ::& rs- bit = (b, bn, bvn) ::& rs+ ait = (an, avn) ::& rs+ bit = (bn, bvn) ::& rs genericIf IF_LEFT U.IF_LEFT mp mq ait bit inp (U.NIL tn vn elMt, i) ->- withUType elMt $ \(elT :: Sing elT, elNotes :: Notes elT) ->- pure $ i :/ NIL ::: ((STList elT, NTList tn elNotes, vn) ::& i)+ withUType elMt $ \elNotes ->+ pure $ i :/ NIL ::: ((NTList tn elNotes, vn) ::& i) - (U.CONS vn, ((at :: Sing a), an, _)- ::& (STList (_ :: Sing a'), ln, _) ::& rs) ->- case eqType @a @a' of+ (U.CONS vn, ((an :: Notes a), _)+ ::& ((ln :: Notes l), _) ::& rs) ->+ case eqType @('TList a) @l of Right Refl -> do n <- onTypeCheckInstrAnnErr uInstr inp "wrong cons type:" (converge ln (NTList def an))- pure $ inp :/ CONS ::: ((STList at, n, vn) ::& rs)+ pure $ inp :/ CONS ::: ((n, vn) ::& rs) Left m -> onTypeCheckInstrErr uInstr (SomeHST inp) ("list element type is different from one " <> "that is being CONSed:") (Left m) - (U.IF_CONS mp mq, (STList a, ns, vn) ::& rs) -> do+ (U.IF_CONS mp mq, (STList{}, ns, vn) ::&+ rs) -> do let NTList _ an = ns- ait = (a, an, vn <> "hd") ::& (STList a, ns, vn <> "tl") ::& rs+ ait = (an, vn <> "hd") ::& (ns, vn <> "tl") ::& rs genericIf IF_CONS U.IF_CONS mp mq ait rs inp - (U.SIZE vn, (STList _, _, _) ::& _) -> sizeImpl inp vn- (U.SIZE vn, (STSet _, _, _) ::& _) -> sizeImpl inp vn- (U.SIZE vn, (STMap _ _, _, _) ::& _) -> sizeImpl inp vn- (U.SIZE vn, (STc SCString, _, _) ::& _) -> sizeImpl inp vn- (U.SIZE vn, (STc SCBytes, _, _) ::& _) -> sizeImpl inp vn+ (U.SIZE vn, (NTList{}, _) ::& _) -> sizeImpl inp vn+ (U.SIZE vn, (NTSet{}, _) ::& _) -> sizeImpl inp vn+ (U.SIZE vn, (NTMap{}, _) ::& _) -> sizeImpl inp vn+ (U.SIZE vn, (NTcs SCString, _) ::& _) -> sizeImpl inp vn+ (U.SIZE vn, (NTcs SCBytes, _) ::& _) -> sizeImpl inp vn (U.EMPTY_SET tn vn (U.Comparable mk ktn), i) -> withSomeSingCT mk $ \k ->- pure $ i :/ EMPTY_SET ::: ((STSet k, NTSet tn ktn, vn) ::& inp)+ pure $ i :/ EMPTY_SET ::: ((STSet k, NTSet tn ktn, vn) ::&+ inp) (U.EMPTY_MAP tn vn (U.Comparable mk ktn) mv, i) ->- withUType mv $ \(v :: Sing v, vns :: Notes v) ->+ withUType mv $ \vns -> withSomeSingCT mk $ \k -> do let ns = NTMap tn ktn vns- pure $ i :/ EMPTY_MAP ::: ((STMap k v, ns, vn) ::& i)+ pure $ i :/ EMPTY_MAP ::: ((STMap k sing, ns, vn) ::&+ i) (U.EMPTY_BIG_MAP tn vn (U.Comparable mk ktn) mv, i) ->- withUType mv $ \(v :: Sing v, vns :: Notes v) ->+ withUType mv $ \vns -> withSomeSingCT mk $ \k -> do let ns = NTBigMap tn ktn vns- pure $ i :/ EMPTY_BIG_MAP ::: ((STBigMap k v, ns, vn) ::& i)+ pure $ i :/ EMPTY_BIG_MAP ::: ((STBigMap k sing, ns, vn) ::&+ i) - (U.MAP vn mp, (STList _, NTList _ vns, _vn) ::& _) -> do+ (U.MAP vn mp, (STList _, NTList _ vns, _vn) ::&+ _) -> do mapImpl vns uInstr mp inp- (\rt rn -> (::&) (STList rt, NTList def rn, vn))+ (\rn -> (::&) (NTList def rn, vn)) - (U.MAP vn mp, (STMap k _, NTMap _ kns vns, _vn) ::& _) -> do+ (U.MAP vn mp, (STMap{}, NTMap _ kns vns, _vn) ::&+ _) -> do let pns = NTPair def def def (NTc kns) vns mapImpl pns uInstr mp inp- (\rt rn -> (::&) (STMap k rt, NTMap def kns rn, vn))+ (\rn -> (::&) (NTMap def kns rn, vn)) -- case `U.HSTER []` is wrongly typed by definition -- (as it is required to at least drop an element), so we don't consider it - (U.ITER (i1 : ir), (STSet _, NTSet _ en, _) ::& _) -> do+ (U.ITER (i1 : ir), (STSet _, NTSet _ en, _) ::&+ _) -> do iterImpl (NTc en) uInstr (i1 :| ir) inp - (U.ITER (i1 : ir), (STList _, NTList _ en, _) ::& _) -> do+ (U.ITER (i1 : ir), (STList _, NTList _ en, _) ::&+ _) -> do iterImpl en uInstr (i1 :| ir) inp - (U.ITER (i1 : ir), (STMap _ _, NTMap _ kns vns, _) ::& _) -> do+ (U.ITER (i1 : ir), (STMap _ _, NTMap _ kns vns, _) ::&+ _) -> do let en = NTPair def def def (NTc kns) vns iterImpl en uInstr (i1 :| ir) inp - (U.MEM vn, (STc _, _, _) ::& (STSet _, _, _) ::& _) ->+ (U.MEM vn, (NTc _, _) ::& (STSet{}, NTSet{}, _) ::&+ _) -> memImpl uInstr inp vn- (U.MEM vn, (STc _, _, _) ::& (STMap _ _, _, _) ::& _) ->+ (U.MEM vn, (NTc _, _) ::& (STMap{}, NTMap{}, _) ::&+ _) -> memImpl uInstr inp vn- (U.MEM vn, (STc _, _, _) ::& (STBigMap _ _, _, _) ::& _) ->+ (U.MEM vn, (NTc _, _) ::& (STBigMap{}, NTBigMap{}, _) ::&+ _) -> memImpl uInstr inp vn - (U.GET vn, _ ::& (STMap _ vt, NTMap _ _ v, _) ::& _) ->- getImpl uInstr inp vt v vn- (U.GET vn, _ ::& (STBigMap _ vt, NTBigMap _ _ v, _) ::& _) ->- getImpl uInstr inp vt v vn+ (U.GET vn, _ ::& (STMap{}, NTMap _ _ v, _) ::&+ _) ->+ getImpl uInstr inp v vn+ (U.GET vn, _ ::& (STBigMap{}, NTBigMap _ _ v, _) ::&+ _) ->+ getImpl uInstr inp v vn - (U.UPDATE vn, _ ::& _ ::& (STMap _ _, _, _) ::& _) ->+ (U.UPDATE vn, _ ::& _ ::& (STMap _ _, _, _) ::&+ _) -> updImpl uInstr inp vn- (U.UPDATE vn, _ ::& _ ::& (STBigMap _ _, _, _) ::& _) ->+ (U.UPDATE vn, _ ::& _ ::& (STBigMap _ _, _, _) ::&+ _) -> updImpl uInstr inp vn- (U.UPDATE vn, _ ::& _ ::& (STSet _, _, _) ::& _) ->+ (U.UPDATE vn, _ ::& _ ::& (STSet _, _, _) ::&+ _) -> updImpl uInstr inp vn - (U.IF mp mq, (STc SCBool, _, _) ::& rs) ->+ (U.IF mp mq, (NTcs SCBool, _) ::& rs) -> genericIf IF U.IF mp mq rs rs inp - (U.LOOP is, (STc SCBool, _, _) ::& (rs :: HST rs)) -> do+ (U.LOOP is, (NTcs SCBool, _) ::& (rs :: HST rs)) -> do _ :/ tp <- lift $ typeCheckList is rs case tp of subI ::: (o :: HST o) -> do@@ -457,49 +440,46 @@ AnyOutInstr subI -> pure $ inp :/ LOOP subI ::: rs - (U.LOOP_LEFT is, (STOr (at :: Sing a) (bt :: Sing b), ons, ovn) ::& rs) -> do+ (U.LOOP_LEFT is, (os@STOr{}, ons, ovn) ::&+ rs) -> do let (an, bn, avn, bvn) = deriveNsOr ons ovn- ait = (at, an, avn) ::& rs+ ait = (an, avn) ::& rs _ :/ tp <- lift $ typeCheckList is ait case tp of subI ::: o -> do- case (eqHST o (sing @('TOr a b) -:& rs), o) of- (Right Refl, ((STOr _ bt', ons', ovn') ::& rs')) -> do+ case (eqHST o (os -:& rs), o) of+ (Right Refl, ((ons', ovn') ::& rs')) -> do let (_, bn', _, bvn') = deriveNsOr ons' ovn' br <- onTypeCheckInstrAnnErr uInstr inp "wrong LOOP_LEFT input type:" $- convergeHSTEl (bt, bn, bvn) (bt', bn', bvn')+ convergeHSTEl (bn, bvn) (bn', bvn') pure $ inp :/ LOOP_LEFT subI ::: (br ::& rs') (Left m, _) -> onTypeCheckInstrErr uInstr (SomeHST inp) "iteration expression has wrong output stack type:" (Left m) AnyOutInstr subI -> do- let br = (bt, bn, bvn)+ let br = (bn, bvn) pure $ inp :/ LOOP_LEFT subI ::: (br ::& rs) - (U.LAMBDA vn imt omt is, i) -> do- withUType imt $ \(it :: Sing it, ins :: Notes ins) ->- withUType omt $ \(ot :: Sing ot, ons :: Notes ons) ->- -- further processing is extracted into another function because- -- otherwise I encountered some weird GHC error with that code- -- located right here- lamImpl uInstr is vn it ins ot ons i+ (U.LAMBDA vn (AsUType ins) (AsUType ons) is, i) -> do+ -- further processing is extracted into another function just not to+ -- litter our main typechecking logic+ lamImpl uInstr is vn ins ons i - (U.EXEC vn, ((_ :: Sing t1), _, _)- ::& ( STLambda (_ :: Sing t1') t2- , NTLambda _ _ t2n+ (U.EXEC vn, ((_ :: Notes t1), _)+ ::& ( STLambda _ _+ , NTLambda _ (_ :: Notes t1') t2n , _ )- ::& rs) -> do+ ::&+ rs) -> do Refl <- onTypeCheckInstrErr uInstr (SomeHST inp) "lambda is given argument with wrong type:" (eqType @t1 @t1')- pure $ inp :/ EXEC ::: ((t2, t2n, vn) ::& rs)+ pure $ inp :/ EXEC ::: ((t2n, vn) ::& rs) - (U.APPLY vn, ((_ :: Sing a'), _, _)- ::& (STLambda (STPair (sa :: Sing a) (sb :: Sing b)) sc, ln, _)- ::& rs) -> do+ (U.APPLY vn, ((_ :: Notes a'), _)+ ::& ( STLambda (STPair sa _) _+ , NTLambda vann (NTPair _ _ _ (_ :: Notes a) (nb :: Notes b)) sc+ , _)+ ::&+ rs) -> do let- l2n = case ln of- NTLambda vann (NTPair _ _ _ _ bn) cn ->- NTLambda vann bn cn+ l2n = NTLambda vann nb sc proofArgEq <- onTypeCheckInstrErr uInstr (SomeHST inp) "lambda is given argument with wrong type:" (eqType @a' @a)@@ -520,7 +500,7 @@ pure (contractTypeAbsense sa) case (proofArgEq, proofOp, proofBigMap, proofContract) of (Refl, Dict, Dict, Dict) ->- pure $ inp :/ (APPLY @a) ::: ((STLambda sb sc, l2n, vn) ::& rs)+ pure $ inp :/ (APPLY @a) ::: ((l2n, vn) ::& rs) (U.DIP is, a ::& s) -> do typeCheckDipBody uInstr is s $@@ -546,109 +526,109 @@ (U.FAILWITH, (_ ::& _)) -> pure $ inp :/ AnyOutInstr FAILWITH - (U.CAST vn (AsUType _ castToNotes), (e, en, evn) ::& rs) -> do+ (U.CAST vn (AsUType castToNotes), (en, evn) ::& rs) -> do (Refl, _) <- errM $ matchTypes en castToNotes- pure $ inp :/ CAST ::: ((e, castToNotes, vn `orAnn` evn) ::& rs)+ pure $ inp :/ CAST ::: ((castToNotes, vn `orAnn` evn) ::& rs) where errM :: (MonadReader InstrCallStack m, MonadError TCError m) => Either TCTypeError a -> m a errM = onTypeCheckInstrErr uInstr (SomeHST inp) "cast to incompatible type:" - (U.RENAME vn, (at, an, _) ::& rs) ->- pure $ inp :/ RENAME ::: ((at, an, vn) ::& rs)+ (U.RENAME vn, (an, _) ::& rs) ->+ pure $ inp :/ RENAME ::: ((an, vn) ::& rs) - (U.UNPACK tn vn mt, (STc SCBytes, _, _) ::& rs) ->- withUType mt $ \(t, tns) -> do+ (U.UNPACK tn vn mt, (NTcs SCBytes, _) ::& rs) ->+ withUType mt $ \tns -> do let ns = NTOption tn tns proofOp <- maybe (typeCheckInstrErr uInstr (SomeHST inp) "Operation cannot appear in serialized data")- pure (opAbsense t)+ pure (opAbsense $ notesSing tns) proofBigMap <- maybe (typeCheckInstrErr uInstr (SomeHST inp) "BigMap cannot appear in serialized data")- pure (bigMapAbsense t)+ pure (bigMapAbsense $ notesSing tns) proofContract <- maybe (typeCheckInstrErr uInstr (SomeHST inp) "UNPACK should not contain 'contract' type")- pure (contractTypeAbsense t)+ pure (contractTypeAbsense $ notesSing tns) case (proofOp, proofBigMap, proofContract) of (Dict, Dict, Dict) ->- pure $ inp :/ UNPACK ::: ((STOption t, ns, vn) ::& rs)+ pure $ inp :/ UNPACK ::: ((ns, vn) ::& rs) - (U.PACK vn, (a, _, _) ::& rs) -> do+ (U.PACK vn, (a, _) ::& rs) -> do proofOp <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "Operations cannot appear in serialized data"- $ Left $ UnsupportedTypes [fromSingT a])- pure (opAbsense a)+ $ Left $ UnsupportedTypes [notesT a])+ pure (opAbsense $ notesSing a) proofBigMap <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "BigMap cannot appear in serialized data"- $ Left $ UnsupportedTypes [fromSingT a])- pure (bigMapAbsense a)+ $ Left $ UnsupportedTypes [notesT a])+ pure (bigMapAbsense $ notesSing a) case (proofOp, proofBigMap) of (Dict, Dict) ->- pure $ inp :/ PACK ::: ((STc SCBytes, starNotes, vn) ::& rs)+ pure $ inp :/ PACK ::: ((starNotes, vn) ::& rs) - (U.CONCAT vn, (STc SCBytes, _, _) ::& (STc SCBytes, _, _) ::& _) ->+ (U.CONCAT vn, (NTcs SCBytes, _) ::& (NTcs SCBytes, _) ::& _) -> concatImpl inp vn- (U.CONCAT vn, (STc SCString, _, _) ::& (STc SCString, _, _) ::& _) ->+ (U.CONCAT vn, (NTcs SCString, _) ::& (NTcs SCString, _) ::& _) -> concatImpl inp vn- (U.CONCAT vn, (STList (STc SCBytes), _, _) ::& _) ->+ (U.CONCAT vn, (STList (STc SCBytes), _, _) ::&+ _) -> concatImpl' inp vn- (U.CONCAT vn, (STList (STc SCString), _, _) ::& _) ->+ (U.CONCAT vn, (STList (STc SCString), _, _) ::&+ _) -> concatImpl' inp vn- (U.SLICE vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::&- (STc SCString, _, _) ::& _) -> sliceImpl inp vn- (U.SLICE vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::&- (STc SCBytes, _, _) ::& _) -> sliceImpl inp vn+ (U.SLICE vn, (NTcs SCNat, _) ::&+ (NTcs SCNat, _) ::&+ (NTcs SCString, _) ::& _) -> sliceImpl inp vn+ (U.SLICE vn, (NTcs SCNat, _) ::&+ (NTcs SCNat, _) ::&+ (NTcs SCBytes, _) ::& _) -> sliceImpl inp vn - (U.ISNAT vn', (STc SCInt, _, oldVn) ::& rs) -> do+ (U.ISNAT vn', (NTcs SCInt, oldVn) ::& rs) -> do let vn = vn' `orAnn` oldVn- pure $ inp :/ ISNAT ::: ((STOption (STc SCNat), starNotes, vn) ::& rs)+ pure $ inp :/ ISNAT ::: ((starNotes, vn) ::& rs) - (U.ADD vn, (STc a, _, _) ::& (STc b, _, _) ::& _) -> addImpl a b inp vn- (U.SUB vn, (STc a, _, _) ::& (STc b, _, _) ::& _) -> subImpl a b inp vn- (U.MUL vn, (STc a, _, _) ::& (STc b, _, _) ::& _) -> mulImpl a b inp vn- (U.EDIV vn, (STc a, _, _) ::& (STc b, _, _) ::& _) -> edivImpl a b inp vn+ (U.ADD vn, (STc a, _, _) ::&+ (STc b, _, _) ::&+ _) -> addImpl a b inp vn+ (U.SUB vn, (STc a, _, _) ::&+ (STc b, _, _) ::&+ _) -> subImpl a b inp vn+ (U.MUL vn, (STc a, _, _) ::&+ (STc b, _, _) ::&+ _) -> mulImpl a b inp vn+ (U.EDIV vn, (STc a, _, _) ::&+ (STc b, _, _) ::&+ _) -> edivImpl a b inp vn - (U.ABS vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Abs ABS inp vn+ (U.ABS vn, (STc SCInt, _, _) ::&+ _) -> unaryArithImpl @Abs ABS inp vn - (U.NEG vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Neg NEG inp vn- (U.NEG vn, (STc SCNat, _, _) ::& _) -> unaryArithImpl @Neg NEG inp vn+ (U.NEG vn, (STc SCInt, _, _) ::&+ _) -> unaryArithImpl @Neg NEG inp vn+ (U.NEG vn, (STc SCNat, _, _) ::&+ _) -> unaryArithImpl @Neg NEG inp vn - (U.LSL vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @Lsl LSL inp vn+ (U.LSL vn, (STc SCNat, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @Lsl LSL inp vn - (U.LSR vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @Lsr LSR inp vn+ (U.LSR vn, (STc SCNat, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @Lsr LSR inp vn - (U.OR vn, (STc SCBool, _, _) ::&- (STc SCBool, _, _) ::& _) -> arithImpl @Or OR inp vn- (U.OR vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @Or OR inp vn+ (U.OR vn, (STc SCBool, _, _) ::&++ (STc SCBool, _, _) ::&+ _) -> arithImpl @Or OR inp vn+ (U.OR vn, (STc SCNat, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @Or OR inp vn - (U.AND vn, (STc SCInt, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @And AND inp vn- (U.AND vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @And AND inp vn- (U.AND vn, (STc SCBool, _, _) ::&- (STc SCBool, _, _) ::& _) -> arithImpl @And AND inp vn+ (U.AND vn, (STc SCInt, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @And AND inp vn+ (U.AND vn, (STc SCNat, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @And AND inp vn+ (U.AND vn, (STc SCBool, _, _) ::&++ (STc SCBool, _, _) ::&+ _) -> arithImpl @And AND inp vn - (U.XOR vn, (STc SCBool, _, _) ::&- (STc SCBool, _, _) ::& _) -> arithImpl @Xor XOR inp vn- (U.XOR vn, (STc SCNat, _, _) ::&- (STc SCNat, _, _) ::& _) -> arithImpl @Xor XOR inp vn+ (U.XOR vn, (STc SCBool, _, _) ::&++ (STc SCBool, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn+ (U.XOR vn, (STc SCNat, _, _) ::&++ (STc SCNat, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn - (U.NOT vn, (STc SCNat, _, _) ::& _) -> unaryArithImpl @Not NOT inp vn- (U.NOT vn, (STc SCBool, _, _) ::& _) -> unaryArithImpl @Not NOT inp vn- (U.NOT vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Not NOT inp vn+ (U.NOT vn, (STc SCNat, _, _) ::&+ _) -> unaryArithImpl @Not NOT inp vn+ (U.NOT vn, (STc SCBool, _, _) ::&+ _) -> unaryArithImpl @Not NOT inp vn+ (U.NOT vn, (STc SCInt, _, _) ::&+ _) -> unaryArithImpl @Not NOT inp vn (U.COMPARE vn,- (a :: Sing aT, an :: Notes aT, _)- ::& (_ :: Sing bT, bn :: Notes bT, _)+ (an :: Notes aT, _)+ ::& (bn :: Notes bT, _) ::& rs ) -> do@@ -659,11 +639,11 @@ maybe (onTypeCheckInstrErr (U.COMPARE vn) (SomeHST inp) "comparison is given incomparable arguments" $ Left $ UnsupportedTypes [demote @aT, demote @bT])- pure (comparabilityPresence a)+ pure (comparabilityPresence $ notesSing an) case proof of Dict -> do- pure $ inp :/ COMPARE ::: ((sing, starNotes, vn) ::& rs)+ pure $ inp :/ COMPARE ::: ((starNotes, vn) ::& rs) Left err -> do typeCheckInstrErr' uInstr (SomeHST inp) "comparing different types:" err@@ -671,144 +651,145 @@ errConv :: (MonadReader InstrCallStack m, MonadError TCError m) => Either AnnConvergeError a -> m a errConv = onTypeCheckInstrAnnErr uInstr inp "comparing types with different annotations:" - (U.EQ vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Eq' EQ inp vn+ (U.EQ vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Eq' EQ inp vn - (U.NEQ vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Neq NEQ inp vn+ (U.NEQ vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Neq NEQ inp vn - (U.LT vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Lt LT inp vn+ (U.LT vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Lt LT inp vn - (U.GT vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Gt GT inp vn+ (U.GT vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Gt GT inp vn - (U.LE vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Le LE inp vn+ (U.LE vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Le LE inp vn - (U.GE vn, (STc SCInt, _, _) ::& _) -> unaryArithImpl @Ge GE inp vn+ (U.GE vn, (NTcs SCInt, _) ::& _) -> unaryArithImpl @Ge GE inp vn - (U.INT vn, (STc SCNat, _, _) ::& rs) ->- pure $ inp :/ INT ::: ((STc SCInt, starNotes, vn) ::& rs)+ (U.INT vn, (NTcs SCNat, _) ::& rs) ->+ pure $ inp :/ INT ::: ((starNotes, vn) ::& rs) (U.SELF vn fn, _) -> do cpType <- gets tcContractParam let t = fromUType cpType -- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has -- valid parameter type- withUType cpType $ \(singcp :: Sing cp, (ParamNotesUnsafe -> notescp)) -> do+ withUType cpType $ \(ParamNotesUnsafe -> notescp :: ParamNotes t) -> do proofOp <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain operation" $ Left $ UnsupportedTypes [t])- pure (opAbsense singcp)+ pure (opAbsense $ sing @t) proofBigMap <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain nested big_map" $ Left $ UnsupportedTypes [t])- pure (nestedBigMapsAbsense singcp)+ pure (nestedBigMapsAbsense $ sing @t) case (proofOp, proofBigMap) of (Dict, Dict) -> do epName <- onTypeCheckInstrErr uInstr (SomeHST inp) "Bad annotation:" $ epNameFromRefAnn fn `onLeft` IllegalEntryPoint MkEntryPointCallRes (argNotes :: Notes arg) epc <-- mkEntryPointCall epName (singcp, notescp)+ mkEntryPointCall epName notescp & maybeToRight (EntryPointNotFound epName) & onTypeCheckInstrErr uInstr (SomeHST inp) "entrypoint not found" let ntRes = NTContract U.noAnn argNotes pure $ inp :/ SELF @arg (SomeEpc epc)- ::: ((sing @('TContract arg), ntRes, vn) ::& inp)+ ::: ((ntRes, vn) ::& inp) - (U.CONTRACT vn fn mt, (STc SCAddress, _, _) ::& rs) ->- withUType mt $ \(t :: Sing t, tns :: Notes t) -> do+ (U.CONTRACT vn fn mt, (NTcs SCAddress, _) ::& rs) ->+ withUType mt $ \(tns :: Notes t) -> do proofOp <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain operation" $ Left $ UnsupportedTypes [fromUType mt])- pure (opAbsense t)+ pure (opAbsense $ sing @t) proofBigMap <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain nested big_map" $ Left $ UnsupportedTypes [fromUType mt])- pure (nestedBigMapsAbsense t)+ pure (nestedBigMapsAbsense $ sing @t) let ns = NTOption def $ NTContract def tns epName <- onTypeCheckInstrErr uInstr (SomeHST inp) "Bad annotation:" $ epNameFromRefAnn fn `onLeft` IllegalEntryPoint case (proofOp, proofBigMap) of (Dict, Dict) ->- pure $ inp :/ CONTRACT tns epName ::: ((STOption $ STContract t, ns, vn) ::& rs)+ pure $ inp :/ CONTRACT tns epName ::: ((ns, vn) ::& rs) - (U.TRANSFER_TOKENS vn, ((_ :: Sing p'), _, _)- ::& (STc SCMutez, _, _) ::& (STContract (p :: Sing p), _, _) ::& rs) -> do+ (U.TRANSFER_TOKENS vn, ((_ :: Notes p'), _)+ ::& (NTcs SCMutez, _)+ ::& (STContract (p :: Sing p), _, _) ::&+ rs) -> do proofOp <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain operation"- $ Left $ UnsupportedTypes [demote @p])+ $ Left $ UnsupportedTypes [fromSingT p]) pure (opAbsense p) proofBigMap <- maybe (onTypeCheckInstrErr uInstr (SomeHST inp) "contract param type cannot contain big_map"- $ Left $ UnsupportedTypes [demote @p])+ $ Left $ UnsupportedTypes [fromSingT p]) pure (nestedBigMapsAbsense p) case (eqType @p @p', proofOp, proofBigMap) of (Right Refl, Dict, Dict) ->- pure $ inp :/ TRANSFER_TOKENS ::: ((STOperation, starNotes, vn) ::& rs)+ pure $ inp :/ TRANSFER_TOKENS ::: ((starNotes, vn) ::& rs) (Left m, _, _) -> onTypeCheckInstrErr uInstr (SomeHST inp) "mismatch of contract param type:" (Left m) - (U.SET_DELEGATE vn, (STOption (STc SCKeyHash), _, _) ::& rs) -> do- pure $ inp :/ SET_DELEGATE ::: ((STOperation, starNotes, vn) ::& rs)+ (U.SET_DELEGATE vn, (STOption (STc SCKeyHash), _, _) ::&+ rs) -> do+ pure $ inp :/ SET_DELEGATE ::: ((starNotes, vn) ::& rs) (U.CREATE_CONTRACT ovn avn contract, (STOption (STc SCKeyHash), _, _)- ::& (STc SCMutez, _, _)- ::& ((_ :: Sing g), gn, _) ::& rs) -> do- (SomeContract (FullContract (contr :: Contract p' g') paramNotes storeNotes))+ ::&+ (NTcs SCMutez, _)+ ::& (gn :: Notes g, _) ::& rs) -> do+ (SomeContract (FullContract (contr :: ContractCode p' g') paramNotes storeNotes)) <- lift $ typeCheckContractImpl contract Refl <- checkEqT @g @g' uInstr inp "contract storage type mismatch" void $ onTypeCheckInstrAnnErr uInstr inp "contract storage type mismatch" (converge gn storeNotes) pure $ inp :/ CREATE_CONTRACT (FullContract contr paramNotes storeNotes) :::- ((STOperation, starNotes, ovn) ::& (STc SCAddress, starNotes, avn) ::& rs)+ ((starNotes, ovn) ::& (starNotes, avn) ::& rs) - (U.IMPLICIT_ACCOUNT vn, (STc SCKeyHash, _, _) ::& rs) ->- pure $ inp :/ IMPLICIT_ACCOUNT ::: ((STContract STUnit, starNotes, vn) ::& rs)+ (U.IMPLICIT_ACCOUNT vn, (NTcs SCKeyHash, _) ::& rs) ->+ pure $ inp :/ IMPLICIT_ACCOUNT ::: ((starNotes, vn) ::& rs) (U.NOW vn, _) ->- pure $ inp :/ NOW ::: ((STc SCTimestamp, starNotes, vn) ::& inp)+ pure $ inp :/ NOW ::: ((starNotes, vn) ::& inp) (U.AMOUNT vn, _) ->- pure $ inp :/ AMOUNT ::: ((STc SCMutez, starNotes, vn) ::& inp)+ pure $ inp :/ AMOUNT ::: ((starNotes, vn) ::& inp) (U.BALANCE vn, _) ->- pure $ inp :/ BALANCE ::: ((STc SCMutez, starNotes, vn) ::& inp)+ pure $ inp :/ BALANCE ::: ((starNotes, vn) ::& inp) (U.CHECK_SIGNATURE vn,- (STKey, _, _)- ::& (STSignature, _, _) ::& (STc SCBytes, _, _) ::& rs) ->- pure $ inp :/ CHECK_SIGNATURE ::: ((STc SCBool, starNotes, vn) ::& rs)+ (NTKey _, _)+ ::& (NTSignature _, _) ::& (NTcs SCBytes, _) ::& rs) ->+ pure $ inp :/ CHECK_SIGNATURE ::: ((starNotes, vn) ::& rs) - (U.SHA256 vn, (STc SCBytes, _, _) ::& rs) ->- pure $ inp :/ SHA256 ::: ((STc SCBytes, starNotes, vn) ::& rs)+ (U.SHA256 vn, (NTcs SCBytes, _) ::& rs) ->+ pure $ inp :/ SHA256 ::: ((starNotes, vn) ::& rs) - (U.SHA512 vn, (STc SCBytes, _, _) ::& rs) ->- pure $ inp :/ SHA512 ::: ((STc SCBytes, starNotes, vn) ::& rs)+ (U.SHA512 vn, (NTcs SCBytes, _) ::& rs) ->+ pure $ inp :/ SHA512 ::: ((starNotes, vn) ::& rs) - (U.BLAKE2B vn, (STc SCBytes, _, _) ::& rs) ->- pure $ inp :/ BLAKE2B ::: ((STc SCBytes, starNotes, vn) ::& rs)+ (U.BLAKE2B vn, (NTcs SCBytes, _) ::& rs) ->+ pure $ inp :/ BLAKE2B ::: ((starNotes, vn) ::& rs) - (U.HASH_KEY vn, (STKey, _, _) ::& rs) ->- pure $ inp :/ HASH_KEY ::: ((STc SCKeyHash, starNotes, vn) ::& rs)+ (U.HASH_KEY vn, (NTKey{}, _) ::& rs) ->+ pure $ inp :/ HASH_KEY ::: ((starNotes, vn) ::& rs) (U.STEPS_TO_QUOTA vn, _) ->- pure $ inp :/ STEPS_TO_QUOTA ::: ((STc SCNat, starNotes, vn) ::& inp)+ pure $ inp :/ STEPS_TO_QUOTA ::: ((starNotes, vn) ::& inp) (U.SOURCE vn, _) ->- pure $ inp :/ SOURCE ::: ((STc SCAddress, starNotes, vn) ::& inp)+ pure $ inp :/ SOURCE ::: ((starNotes, vn) ::& inp) (U.SENDER vn, _) ->- pure $ inp :/ SENDER ::: ((STc SCAddress, starNotes, vn) ::& inp)+ pure $ inp :/ SENDER ::: ((starNotes, vn) ::& inp) - (U.ADDRESS vn, (STContract _, _, _) ::& rs) ->- pure $ inp :/ ADDRESS ::: ((STc SCAddress, starNotes, vn) ::& rs)+ (U.ADDRESS vn, (NTContract{}, _) ::& rs) ->+ pure $ inp :/ ADDRESS ::: ((starNotes, vn) ::& rs) (U.CHAIN_ID vn, _) ->- pure $ inp :/ CHAIN_ID ::: ((STChainId, starNotes, vn) ::& inp)+ pure $ inp :/ CHAIN_ID ::: ((starNotes, vn) ::& inp) _ -> typeCheckInstrErr uInstr (SomeHST inp) "unknown expression"@@ -859,17 +840,17 @@ -> [U.ExpandedOp] -> HST (c ': rs) -> (forall v' . (Typeable v', SingI v') =>- Sing v' -> Notes v' -> HST rs -> HST (MapOpRes c v' ': rs))+ Notes v' -> HST rs -> HST (MapOpRes c v' ': rs)) -> TypeCheckInstr (SomeInstr (c ': rs)) mapImpl vn instr mp i@(_ ::& rs) mkRes = do- _ :/ subp <- lift $ typeCheckList mp ((sing, vn, def) ::& rs)+ _ :/ subp <- lift $ typeCheckList mp ((vn, def) ::& rs) case subp of sub ::: subo -> case subo of- (b, bn, _bvn) ::& rs' -> do+ (bn, _bvn) ::& rs' -> do Refl <- checkEqHST rs rs' instr i $ "map expression has changed not only top of the stack"- pure $ i :/ MAP sub ::: mkRes b bn rs'+ pure $ i :/ MAP sub ::: mkRes bn rs' _ -> typeCheckInstrErr instr (SomeHST i) "map expression has wrong output stack type (empty stack)" AnyOutInstr _ -> typeCheckInstrErr instr (SomeHST i) "MAP code block always fails, which is not allowed"@@ -885,9 +866,9 @@ -> NonEmpty U.ExpandedOp -> HST (c ': rs) -> TypeCheckInstr (SomeInstr (c ': rs))-iterImpl en instr mp i@((_, _, lvn) ::& rs) = do+iterImpl en instr mp i@((_, lvn) ::& rs) = do let evn = deriveVN "elt" lvn- _ :/ subp <- lift $ typeCheckNE mp ((sing, en, evn) ::& rs)+ _ :/ subp <- lift $ typeCheckNE mp ((en, evn) ::& rs) case subp of subI ::: o -> do Refl <- checkEqHST o rs instr i@@ -904,21 +885,21 @@ => U.ExpandedInstr -> [U.ExpandedOp] -> VarAnn- -> Sing it -> Notes it- -> Sing ot -> Notes ot+ -> Notes it+ -> Notes ot -> HST ts -> TypeCheckInstr (SomeInstr ts)-lamImpl instr is vn it ins ot ons i = do+lamImpl instr is vn ins ons i = do when (any hasSelf is) $ typeCheckInstrErr instr (SomeHST i) "The SELF instruction cannot appear in a lambda"- _ :/ lamI <- lift $ typeCheckList is ((it, ins, def) ::& SNil)+ _ :/ lamI <- lift $ typeCheckList is ((ins, def) ::& SNil) let lamNotes onsr = NTLambda def ins onsr- let lamSt onsr = (STLambda it ot, lamNotes onsr, vn) ::& i+ let lamSt onsr = (lamNotes onsr, vn) ::& i fmap (i :/) $ case lamI of lam ::: lo -> do case eqHST1 @ot lo of Right Refl -> do- let (_, ons', _) ::& SNil = lo+ let (ons', _) ::& SNil = lo onsr <- onTypeCheckInstrAnnErr instr i "wrong output type of lambda's expression:" (converge ons ons') pure (LAMBDA (VLam $ RfNormal lam) ::: lamSt onsr) Left m -> onTypeCheckInstrErr instr (SomeHST i)
src/Michelson/TypeCheck/Types.hs view
@@ -1,12 +1,11 @@ module Michelson.TypeCheck.Types ( HST (..) , (-:&)+ , pattern (::&+) , SomeHST (..) , SomeInstrOut (..) , SomeInstr (..)- , SomeNotedValue (..) , SomeContract (..)- , SomeCValue (..) , BoundVars (..) , TcExtFrames , mapSomeContract@@ -21,10 +20,9 @@ import Michelson.Untyped (Var, Type, noAnn) import Michelson.Untyped.Annotation (VarAnn) import Michelson.Typed- (Notes(..), Sing(..), T(..), fromSingT, starNotes)+ (Notes(..), Sing(..), T(..), starNotes, notesT) import qualified Michelson.Typed as T import Michelson.Typed.Instr-import Michelson.Typed.Value import Util.Typeable -- | Data type holding type information for stack (Heterogeneous Stack Type).@@ -44,35 +42,37 @@ -- These applications of @Typeable@ class are required for convenient usage -- of type encoded by @HST ts@ with some functions from @Data.Typeable@. ----- Data type @HST@ (Heterogeneous Stack Type) is a heterogenuous list of triples.--- First element of triple is a type singleton which is due to main motivation--- behind @HST@, namely for it to be used as representation of @Instr@ type--- data for pattern-matching.--- Second element of triple is a structure, holding field and type annotations+-- Data type @HST@ (Heterogeneous Stack Type) is a heterogenuous list of tuples.+-- First element of tuple is a structure, holding field and type annotations -- for a given type.--- Third element of triple is an optional variable annotation for the stack+-- Second element of tuple is an optional variable annotation for the stack -- element.+-- Additionally constructor keeps 'SingI' constraint for the current type. data HST (ts :: [T]) where SNil :: HST '[] (::&) :: (Typeable xs, Typeable x, SingI x)- => (Sing x, Notes x, VarAnn)+ => (Notes x, VarAnn) -> HST xs -> HST (x ': xs) +instance NFData (HST ts) where+ rnf (SNil) = ()+ rnf ((a, b) ::& hst) = rnf (a, b, hst)+ instance Show (HST ts) where show SNil = "[]" show (r ::& rs) = "[ " <> showDo (r ::& rs) <> " ]" where showDo :: HST (t ': ts_) -> String- showDo ((a, _notes, _vn) ::& (b ::& c)) =- show (fromSingT a) <> ", " <> showDo (b ::& c)- showDo ((a, _notes, _vn) ::& SNil) = show (fromSingT a)+ showDo ((notesT -> t, _vn) ::& (b ::& c)) =+ show t <> ", " <> showDo (b ::& c)+ showDo ((notesT -> t, _vn) ::& SNil) = show t infixr 7 ::& instance Eq (HST ts) where SNil == SNil = True- (_, n1, a1) ::& h1 == (_, n2, a2) ::& h2 =+ (n1, a1) ::& h1 == (n2, a2) ::& h2 = n1 == n2 && a1 == a2 && h1 == h2 -- | Append a type to 'HST', assuming that notes and annotations@@ -82,14 +82,29 @@ => Sing x -> HST xs -> HST (x ': xs)-s -:& hst = (s, starNotes, noAnn) ::& hst+_ -:& hst = (starNotes, noAnn) ::& hst infixr 7 -:& +-- | Extended pattern-match - adds @Sing x@ argument.+infixr 7 ::&++pattern (::&+)+ :: ()+ => ( ys ~ (x ': xs)+ , Typeable x, SingI x, Typeable xs+ )+ => (Sing x, Notes x, VarAnn)+ -> HST xs+ -> HST ys+pattern x ::&+ hst <- ((\(n, v) -> (T.notesSing n, n, v)) -> x) ::& hst+ where (_, n, v) ::&+ hst = (n, v) ::& hst+ -- | No-argument type wrapper for @HST@ data type. data SomeHST where SomeHST :: Typeable ts => HST ts -> SomeHST deriving stock instance Show SomeHST+instance NFData SomeHST where+ rnf (SomeHST h) = rnf h instance Eq SomeHST where SomeHST hst1 == SomeHST hst2 = hst1 `eqParam1` hst2@@ -132,27 +147,11 @@ instance Show (ExtInstr inp) => Show (SomeInstr inp) where show (inp :/ out) = show inp <> " -> " <> show out --- | Data type, holding strictly-typed Michelson value along with its--- type singleton.-data SomeNotedValue where- (::::) :: (SingI t, Typeable t)- => T.Value t- -> (Sing t, Notes t)- -> SomeNotedValue--instance Show SomeNotedValue where- show (val :::: (sing, notes)) =- show val <> " :: " <> "(" <> show (fromSingT sing)- <> ", " <> show notes <> ")"---- | Data type, holding strictly-typed Michelson value along with its--- type singleton.-data SomeCValue where- (:--:) :: (SingI t, Typeable t)- => CValue t -> Sing t -> SomeCValue- data SomeContract where SomeContract :: FullContract cp st -> SomeContract++instance NFData SomeContract where+ rnf (SomeContract c) = rnf c mapSomeContract :: (forall inp out. Instr inp out -> Instr inp out)
src/Michelson/TypeCheck/Value.hs view
@@ -8,7 +8,7 @@ import Data.Default (def) import qualified Data.Map as M import qualified Data.Set as S-import Data.Singletons (SingI(..))+import Data.Singletons (SingI(..), demote) import Data.Typeable ((:~:)(..)) import Prelude hiding (EQ, GT, LT) @@ -18,8 +18,8 @@ import Michelson.TypeCheck.TypeCheck (TcInstrHandler, TypeCheckEnv(..), TypeCheckInstr) import Michelson.TypeCheck.Types import Michelson.Typed- (pattern AsUType, CT(..), CValue(..), EpAddress(..), Notes(..), ParamNotes(..), Sing(..),- Value'(..), converge, fromSingCT, fromSingT, starNotes)+ (pattern AsUTypeExt, CValue(..), EpAddress(..), Notes(..), Sing(..), Value'(..), fromSingCT,+ fromSingT, starNotes) import qualified Michelson.Typed as T import qualified Michelson.Untyped as U import Tezos.Address (Address(..))@@ -27,46 +27,42 @@ import Tezos.Crypto (parseKeyHash, parsePublicKey, parseSignature) typeCheckCValue- :: U.Value' op -> CT -> Either (U.Value' op, TCTypeError) SomeCValue+ :: forall op ct+ . U.Value' op -> Sing ct -> Either (U.Value' op, TCTypeError) (CValue ct) typeCheckCValue val ct = case (val, ct) of- (U.ValueInt i, CInt) -> pure $ CvInt i :--: SCInt- (U.ValueInt i, CNat)- | i >= 0 -> pure $ CvNat (fromInteger i) :--: SCNat+ (U.ValueInt i, SCInt) -> pure $ CvInt i+ (U.ValueInt i, SCNat)+ | i >= 0 -> pure $ CvNat (fromInteger i) | otherwise -> Left (U.ValueInt i, NegativeNat)- (v@(U.ValueInt i), CMutez) -> do+ (v@(U.ValueInt i), SCMutez) -> do mtz <- maybeToRight (v, InvalidTimestamp) . mkMutez $ fromInteger i- pure $ CvMutez mtz :--: SCMutez- (U.ValueString s, CString) ->- pure $ CvString s :--: SCString- (v@(U.ValueString s), CAddress) -> do+ pure $ CvMutez mtz+ (U.ValueString s, SCString) ->+ pure $ CvString s+ (v@(U.ValueString s), SCAddress) -> do addr <- T.parseEpAddress (unMText s) `onLeft` ((v, ) . InvalidAddress)- pure $ CvAddress addr :--: SCAddress- (v@(U.ValueString s), CKeyHash) -> do+ pure $ CvAddress addr+ (v@(U.ValueString s), SCKeyHash) -> do kHash <- parseKeyHash (unMText s) `onLeft` ((v, ) . InvalidKeyHash)- pure $ CvKeyHash kHash :--: SCKeyHash- (v@(U.ValueString s), CTimestamp) -> do+ pure $ CvKeyHash kHash+ (v@(U.ValueString s), SCTimestamp) -> do tstamp <- maybeToRight (v, InvalidTimestamp) . parseTimestamp $ unMText s- pure $ CvTimestamp tstamp :--: SCTimestamp- (U.ValueInt i, CTimestamp) ->- pure $ CvTimestamp (timestampFromSeconds i) :--: SCTimestamp- (U.ValueBytes (U.InternalByteString s), CBytes) ->- pure $ CvBytes s :--: SCBytes- (U.ValueTrue, CBool) -> pure $ CvBool True :--: SCBool- (U.ValueFalse, CBool) -> pure $ CvBool False :--: SCBool+ pure $ CvTimestamp tstamp+ (U.ValueInt i, SCTimestamp) ->+ pure $ CvTimestamp (timestampFromSeconds i)+ (U.ValueBytes (U.InternalByteString s), SCBytes) ->+ pure $ CvBytes s+ (U.ValueTrue, SCBool) -> pure $ CvBool True+ (U.ValueFalse, SCBool) -> pure $ CvBool False (v, t) ->- Left $ (v, InvalidValueType (T.Tc t))+ Left $ (v, InvalidValueType $ T.Tc $ fromSingCT t) typeCheckCVals- :: forall t op . (Typeable t, SingI t)- => [U.Value' op]- -> CT+ :: forall t op+ . [U.Value' op]+ -> Sing t -> Either (U.Value' op, TCTypeError) [CValue t]-typeCheckCVals mvs t = traverse check mvs- where- check mv = do- v :--: (_ :: Sing t') <- typeCheckCValue mv t- Refl <- eqType @('T.Tc t) @('T.Tc t') `onLeft` (,) mv- pure v+typeCheckCVals mvs t = traverse (`typeCheckCValue` t) mvs tcFailedOnValue :: U.Value -> T.T -> Text -> Maybe TCTypeError -> TypeCheckInstr a tcFailedOnValue v t msg err = do@@ -77,7 +73,7 @@ -- given in representation from @Michelson.Type@ module to representation -- in strictly typed GADT. ----- As a third argument, @typeCheckValImpl@ accepts expected type of value.+-- @typeCheckValImpl@ is polymorphic in the expected type of value. -- -- Type checking algorithm pattern-matches on parse value representation, -- expected type @t@ and constructs @Val t@ value.@@ -86,35 +82,36 @@ -- that is interpreted as input of wrong type and type check finishes with -- error. typeCheckValImpl- :: TcInstrHandler+ :: forall ty.+ SingI ty+ => TcInstrHandler -> U.Value- -> (Sing t, Notes t)- -> TypeCheckInstr SomeNotedValue-typeCheckValImpl tcDo uvalue ty@(tySing, tyNotes) = case (uvalue, tySing, tyNotes) of- (mv, t@(STc ct), NTc nt) -> do- case typeCheckCValue mv (fromSingCT ct) of- Left (uval, err) -> tcFailedOnValue uval (fromSingT $ t) "" (Just err)- Right (v :--: cst) -> pure $ VC v :::: (STc cst, NTc nt)- (U.ValueString (parsePublicKey . unMText -> Right s), STKey, _) ->- pure $ T.VKey s :::: ty+ -> TypeCheckInstr (T.Value ty)+typeCheckValImpl tcDo uvalue = case (uvalue, sing @ty) of+ (mv, t@(STc ct)) -> do+ case typeCheckCValue mv ct of+ Left (uval, err) -> tcFailedOnValue uval (fromSingT t) "" (Just err)+ Right v -> pure $ VC v+ (U.ValueString (parsePublicKey . unMText -> Right s), STKey) ->+ pure $ T.VKey s - (U.ValueString (parseSignature . unMText -> Right s), STSignature, _) ->- pure $ VSignature s :::: ty+ (U.ValueString (parseSignature . unMText -> Right s), STSignature) ->+ pure $ VSignature s - (U.ValueString (parseChainId . unMText -> Right ci), STChainId, _) ->- pure $ VChainId ci :::: ty- (U.ValueBytes (mkChainId . U.unInternalByteString -> Just ci), STChainId, _) ->- pure $ VChainId ci :::: ty+ (U.ValueString (parseChainId . unMText -> Right ci), STChainId) ->+ pure $ VChainId ci+ (U.ValueBytes (mkChainId . U.unInternalByteString -> Just ci), STChainId) ->+ pure $ VChainId ci ( cv@(U.ValueString (T.parseEpAddress . unMText -> Right epAddr)),- STContract (pc :: Sing cp), NTContract _ pn ) -> do+ STContract (pc :: Sing cp)) -> do instrPos <- ask contracts <- gets tcContracts- let ensureTypeMatches :: (Typeable t, SingI t) => (Sing t, Notes t) -> TypeCheckInstr (cp :~: t)- ensureTypeMatches (_, pn') =+ let ensureTypeMatches :: forall t'. (Typeable t', SingI t') => TypeCheckInstr (cp :~: t')+ ensureTypeMatches = liftEither @_ @TypeCheckInstr $- first (TCFailedOnValue cv (fromSingT tySing) "wrong contract parameter" instrPos . Just) $- fmap fst $ matchTypes pn pn'+ first (TCFailedOnValue cv (demote @ty) "wrong contract parameter" instrPos . Just) $+ eqType @cp @t' let unsupportedType :: Text -> Either TCError a unsupportedType desc = Left $@@ -123,97 +120,94 @@ let EpAddress addr epName = epAddr case addr of KeyAddress _ -> do- Refl <- ensureTypeMatches (second unParamNotes T.tyImplicitAccountParam)- pure $ VContract addr T.sepcPrimitive :::: ty+ Refl <- ensureTypeMatches @'T.TUnit+ pure $ VContract addr T.sepcPrimitive ContractAddress ca -> case M.lookup ca contracts of -- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has -- valid parameter type- Just (AsUType cpSing (T.ParamNotesUnsafe -> cpNotes)) -> do+ Just (AsUTypeExt cpSing (T.ParamNotesUnsafe -> cpNotes)) -> do Dict <- liftEither $ maybe (unsupportedType "Operation") pure (T.opAbsense cpSing) Dict <- liftEither $ maybe (unsupportedType "Nested BigMaps") pure (T.nestedBigMapsAbsense cpSing)- case T.mkEntryPointCall epName (cpSing, cpNotes) of+ case T.mkEntryPointCall epName cpNotes of Nothing -> throwError $- TCFailedOnValue cv (fromSingT tySing) "unknown entrypoint" instrPos . Just $+ TCFailedOnValue cv (demote @ty) "unknown entrypoint" instrPos . Just $ EntryPointNotFound epName- Just (T.MkEntryPointCallRes argNotes epc) -> do- ensureTypeMatches (sing, argNotes)- pure $ VContract addr (T.SomeEpc epc) :::: (sing, NTContract U.noAnn argNotes)+ Just (T.MkEntryPointCallRes (_ :: Notes t') epc) -> do+ Refl <- ensureTypeMatches @t'+ pure $ VContract addr (T.SomeEpc epc) Nothing ->- throwError $ TCFailedOnValue cv (fromSingT tySing) "Contract literal unknown"+ throwError $ TCFailedOnValue cv (demote @ty) "Contract literal unknown" instrPos (Just $ UnknownContract addr) - (U.ValueUnit, STUnit, _) -> pure $ VUnit :::: ty- (U.ValuePair ml mr, STPair lt rt, NTPair n1 n2 n3 nl nr) -> do- l :::: (lst, ln) <- typeCheckValImpl tcDo ml (lt, nl)- r :::: (rst, rn) <- typeCheckValImpl tcDo mr (rt, nr)- pure $ VPair (l, r) :::: (STPair lst rst, NTPair n1 n2 n3 ln rn)- (U.ValueLeft ml, STOr lt rt, NTOr n1 n2 n3 nl nr) -> do- l :::: (lst, ln) <- typeCheckValImpl tcDo ml (lt, nl)- pure $ VOr (Left l) :::: ( STOr lst rt, NTOr n1 n2 n3 ln nr)- (U.ValueRight mr, STOr lt rt, NTOr n1 n2 n3 nl nr) -> do- r :::: (rst, rn) <- typeCheckValImpl tcDo mr (rt, nr)- pure $ VOr (Right r) :::: ( STOr lt rst, NTOr n1 n2 n3 nl rn)- (U.ValueSome mv, STOption vt, NTOption na nt) -> do- v :::: (vst, vns) <- typeCheckValImpl tcDo mv (vt, nt)- pure $ VOption (Just v) :::: (STOption vst, NTOption na vns)- (U.ValueNone, STOption _, _) -> do- pure $ VOption Nothing :::: ty+ (U.ValueUnit, STUnit) -> pure $ VUnit+ (U.ValuePair ml mr, STPair _ _) -> do+ l <- typeCheckValImpl tcDo ml+ r <- typeCheckValImpl tcDo mr+ pure $ VPair (l, r)+ (U.ValueLeft ml, STOr{}) -> do+ l <- typeCheckValImpl tcDo ml+ pure $ VOr (Left l)+ (U.ValueRight mr, STOr{}) -> do+ r <- typeCheckValImpl tcDo mr+ pure $ VOr (Right r)+ (U.ValueSome mv, STOption{}) -> do+ v <- typeCheckValImpl tcDo mv+ pure $ VOption (Just v)+ (U.ValueNone, STOption _) -> do+ pure $ VOption Nothing - (U.ValueNil, STList _, _) ->- pure $ T.VList [] :::: ty+ (U.ValueNil, STList _) ->+ pure $ T.VList [] - (U.ValueSeq (toList -> mels), STList vt, NTList _ x) -> do- (els, _) <- typeCheckValsImpl tcDo mels (vt, x)- pure $ VList els :::: ty+ (U.ValueSeq (toList -> mels), STList _) -> do+ els <- typeCheckValsImpl tcDo mels+ pure $ VList els - (U.ValueNil, STSet _, _) ->- pure $ T.VSet S.empty :::: ty+ (U.ValueNil, STSet _) ->+ pure $ T.VSet S.empty - (sq@(U.ValueSeq (toList -> mels)), STSet vt, _) -> do+ (sq@(U.ValueSeq (toList -> mels)), STSet vt) -> do instrPos <- ask- els <- liftEither $ typeCheckCVals mels (fromSingCT vt)+ els <- liftEither $ typeCheckCVals mels vt `onLeft` \(cv, err) -> TCFailedOnValue cv (fromSingT $ STc vt) "wrong type of set element:" instrPos (Just err) elsS <- liftEither $ S.fromDistinctAscList <$> ensureDistinctAsc id els `onLeft` \msg -> TCFailedOnValue sq (fromSingT $ STc vt) msg instrPos Nothing- pure $ VSet elsS :::: ty+ pure $ VSet elsS - (U.ValueNil, STMap _ _, _) -> pure $ T.VMap M.empty :::: ty+ (U.ValueNil, STMap _ _) -> pure $ T.VMap M.empty - (sq@(U.ValueMap (toList -> mels)), STMap kt vt, NTMap _ _ vn) -> do- keyOrderedElts <- typeCheckMapVal tcDo mels sq vn kt vt- pure $ VMap (M.fromDistinctAscList keyOrderedElts) :::: ty+ (sq@(U.ValueMap (toList -> mels)), STMap kt _) -> do+ keyOrderedElts <- typeCheckMapVal tcDo mels sq kt+ pure $ VMap (M.fromDistinctAscList keyOrderedElts) - (U.ValueNil, STBigMap _ _ , _) ->- pure $ T.VBigMap M.empty :::: ty+ (U.ValueNil, STBigMap _ _) ->+ pure $ T.VBigMap M.empty - (sq@(U.ValueMap (toList -> mels)), STBigMap kt vt, NTBigMap _ _ vn) -> do- keyOrderedElts <- typeCheckMapVal tcDo mels sq vn kt vt- pure $ VBigMap (M.fromDistinctAscList keyOrderedElts) :::: ty+ (sq@(U.ValueMap (toList -> mels)), STBigMap kt _) -> do+ keyOrderedElts <- typeCheckMapVal tcDo mels sq kt+ pure $ VBigMap (M.fromDistinctAscList keyOrderedElts) - (v, STLambda (it :: Sing it) (ot :: Sing ot), NTLambda vn _ _) -> do+ (v, STLambda (_ :: Sing it) (_ :: Sing ot)) -> do mp <- case v of U.ValueNil -> pure [] U.ValueLambda mp -> pure $ toList mp- _ -> tcFailedOnValue v (fromSingT tySing) "unexpected value" Nothing- li :/ instr <- typeCheckImpl tcDo mp ((it, starNotes, def) ::& SNil)- let (_, ins, _) ::& SNil = li- let lamS = STLambda it ot+ _ -> tcFailedOnValue v (demote @ty) "unexpected value" Nothing+ _ :/ instr <- typeCheckImpl tcDo mp ((starNotes @it, def) ::& SNil) case instr of lam ::: (lo :: HST lo) -> do case eqHST1 @ot lo of Right Refl -> do- let (_, ons, _) ::& SNil = lo- pure $ VLam (T.RfNormal lam) :::: (STLambda it ot, NTLambda vn ins ons)+ pure $ VLam (T.RfNormal lam) Left m ->- tcFailedOnValue v (fromSingT tySing)+ tcFailedOnValue v (demote @ty) "wrong output type of lambda's value:" (Just m) AnyOutInstr lam ->- pure $ VLam (T.RfAlwaysFails lam) :::: (lamS, NTLambda def ins starNotes)+ pure $ VLam (T.RfAlwaysFails lam) - (v, t, _) -> tcFailedOnValue v (fromSingT t) "unknown value" Nothing+ (v, t) -> tcFailedOnValue v (fromSingT t) "unknown value" Nothing -- | Function @typeCheckMapVal@ typechecks given list of @Elt@s and -- ensures, that its keys are in ascending order.@@ -221,38 +215,31 @@ -- It return list of pairs (key, value) with keys in ascending order -- so it is safe to call @fromDistinctAscList@ on returned list typeCheckMapVal- :: (SingI kt, Typeable kt, SingI vt, Typeable vt)+ :: (SingI kt, Typeable kt, SingI vt) => TcInstrHandler -> [U.Elt U.ExpandedOp] -> U.Value- -> Notes vt -> Sing kt- -> Sing vt -> TypeCheckInstr [(CValue kt, T.Value vt)]-typeCheckMapVal tcDo mels sq vn kt vt = do+typeCheckMapVal tcDo mels sq kt = do instrPos <- ask- ks <- liftEither $ typeCheckCVals (map (\(U.Elt k _) -> k) mels) (fromSingCT kt)+ ks <- liftEither $ typeCheckCVals (map (\(U.Elt k _) -> k) mels) kt `onLeft` \(cv, err) -> TCFailedOnValue cv (fromSingT $ STc kt) "wrong type of map key:" instrPos (Just err)- (vals, _) <- typeCheckValsImpl tcDo (map (\(U.Elt _ v) -> v) mels) (vt, vn)+ vals <- typeCheckValsImpl tcDo (map (\(U.Elt _ v) -> v) mels) ksS <- liftEither $ ensureDistinctAsc id ks `onLeft` \msg -> TCFailedOnValue sq (fromSingT $ STc kt) msg instrPos Nothing pure $ zip ksS vals typeCheckValsImpl- :: forall t . (Typeable t, SingI t)+ :: forall t . (SingI t) => TcInstrHandler -> [U.Value]- -> (Sing t, Notes t)- -> TypeCheckInstr ([T.Value t], Notes t)-typeCheckValsImpl tcDo mvs (t, nt) =- fmap (first reverse) $ foldM check ([], nt) mvs+ -> TypeCheckInstr [T.Value t]+typeCheckValsImpl tcDo mvs =+ fmap reverse $ foldM check [] mvs where- check (res, ns) mv = do- instrPos <- ask- v :::: ((_ :: Sing t'), vns) <- typeCheckValImpl tcDo mv (t, nt)- Refl <- liftEither $ eqType @t @t' `onLeft`- (TCFailedOnValue mv (fromSingT t) "wrong element type" instrPos . Just)- ns' <- liftEither $ converge ns vns `onLeft`- ((TCFailedOnValue mv (fromSingT t) "wrong element type") instrPos . Just . AnnError)- pure (v : res, ns')+ check :: [T.Value t] -> U.Value -> TypeCheckInstr [T.Value t]+ check res mv = do+ v <- typeCheckValImpl @t tcDo mv+ pure (v : res)
src/Michelson/Typed/Annotation.hs view
@@ -22,16 +22,25 @@ , orAnn , isStar , starNotes+ , notesSing+ , notesT+ , pattern NTcs ) where import Data.Default (Default(..)) import qualified Data.Kind as Kind import Data.Singletons (SingI(..)) import Fmt (Buildable(..), (+|), (|+))+import Text.PrettyPrint.Leijen.Text (Doc, (<+>))+import qualified Text.Show +import Michelson.Printer.Util+ (RenderDoc(..), addParens, buildRenderDoc, doesntNeedParens, needsParens, printDocS) import Michelson.Typed.Sing import Michelson.Typed.T (T(..))-import Michelson.Untyped.Annotation (Annotation, FieldAnn, TypeAnn, noAnn, unifyAnn)+import Michelson.Untyped.Annotation+ (Annotation, FieldAnn, TypeAnn, fullAnnSet, noAnn, singleAnnSet, unifyAnn)+import Util.TH import Util.Typeable -- | Data type, holding annotation data for a given Michelson type @t@.@@ -58,9 +67,61 @@ NTMap :: TypeAnn -> TypeAnn -> Notes v -> Notes ('TMap k v) NTBigMap :: TypeAnn -> TypeAnn -> Notes v -> Notes ('TBigMap k v) -deriving stock instance Show (Notes t) deriving stock instance Eq (Notes t)+$(deriveGADTNFData ''Notes) ++instance Show (Notes t) where+ show = printDocS True . renderDoc doesntNeedParens++instance Buildable (Notes t) where+ build = buildRenderDoc++instance RenderDoc (Notes t) where+ renderDoc pn n = addParens pn $ case n of+ NTc ta -> "NTc" <+> rendT ta+ NTKey ta -> "NTKey" <+> rendT ta+ NTUnit ta -> "NTUnit" <+> rendT ta+ NTSignature ta -> "NTSignature" <+> rendT ta+ NTChainId ta -> "NTChainId" <+> rendT ta+ NTOption ta nt -> "NTOption" <+> rendT ta <+> rendN nt+ NTList ta nt -> "NTList" <+> rendT ta <+> rendN nt+ NTSet ta1 ta2 -> "NTSet" <+> rendT2 ta1 ta2+ NTOperation ta -> "NTOperation" <+> rendT ta+ NTContract ta nt -> "NTContract" <+> rendT ta <+> rendN nt+ NTPair ta fa1 fa2 np nq -> "NTPair" <+> rendTF2 ta fa1 fa2 <+> rendN np <+> rendN nq+ NTOr ta fa1 fa2 np nq -> "NTOr" <+> rendTF2 ta fa1 fa2 <+> rendN np <+> rendN nq+ NTLambda ta np nq -> "NTLambda" <+> rendT ta <+> rendN np <+> rendN nq+ NTMap ta1 ta2 nv -> "NTMap" <+> rendT2 ta1 ta2 <+> rendN nv+ NTBigMap ta1 ta2 nv -> "NTBigMap" <+> rendT2 ta1 ta2 <+> rendN nv+ where+ rendN :: Notes n -> Doc+ rendN = renderDoc needsParens++ rendT :: TypeAnn -> Doc+ rendT = renderDoc doesntNeedParens . singleAnnSet++ rendT2 :: TypeAnn -> TypeAnn -> Doc+ rendT2 t1 t2 = renderDoc doesntNeedParens $ fullAnnSet [t1, t2] [] []++ rendTF2 :: TypeAnn -> FieldAnn -> FieldAnn -> Doc+ rendTF2 t f1 f2 = renderDoc doesntNeedParens $ fullAnnSet [t] [f1, f2] []++-- | Forget information about annotations, pick singleton with the same type.+--+-- Note: currently we cannot derive 'Sing' from 'Notes' without 'SingI' because+-- for comparable types notes do not remember which exact comparable was used.+notesSing :: SingI t => Notes t -> Sing t+notesSing _ = sing++-- | Get term-level type of notes.+notesT :: SingI t => Notes t -> T+notesT = fromSingT . notesSing++-- | Similar to 'NTc' pattern, but gives singleton of the comparable type.+pattern NTcs :: SingI t => (t ~ 'Tc ct) => Sing ct -> Notes t+pattern NTcs s <- (notesSing -> STc s)+ -- | In memory of `NStar` constructor. -- Generates notes with no annotations. starNotes :: forall t. SingI t => Notes t@@ -146,3 +207,6 @@ => Annotation tag -> Annotation tag -> Either AnnConvergeError (Annotation tag) convergeAnns a b = maybe (Left $ AnnConvergeError a b) pure $ unifyAnn a b++$(deriveGADTNFData ''AnnConvergeError)+
src/Michelson/Typed/Arith.hs view
@@ -29,7 +29,7 @@ , compareOp ) where -import Data.Bits (complement, shift, xor, (.&.), (.|.))+import Data.Bits (complement, shift, (.&.), (.|.)) import Data.Singletons (SingI(..)) import Fmt (Buildable(build)) @@ -64,14 +64,18 @@ | SubUnderflow | LslOverflow | LsrUnderflow- deriving stock (Show, Eq, Ord)+ deriving stock (Show, Eq, Ord, Generic) +instance NFData ArithErrorType+ -- | Represents an arithmetic error of the operation. data ArithError n m = MutezArithError ArithErrorType n m | ShiftArithError ArithErrorType n m- deriving stock (Show, Eq, Ord)+ deriving stock (Show, Eq, Ord, Generic) +instance (NFData n, NFData m) => NFData (ArithError n m)+ -- | Marker data type for add operation. class UnaryArithOp aop (n :: CT) where type UnaryArithRes aop n :: CT@@ -192,8 +196,8 @@ evalOp _ (CvBool i) (CvBool j) = Right $ CvBool (i .|. j) instance ArithOp And 'CInt 'CNat where- type ArithRes And 'CInt 'CNat = 'CInt- evalOp _ (CvInt i) (CvNat j) = Right $ CvInt (i .&. fromIntegral j)+ type ArithRes And 'CInt 'CNat = 'CNat+ evalOp _ (CvInt i) (CvNat j) = Right $ CvNat ((fromInteger i) .&. j) instance ArithOp And 'CNat 'CNat where type ArithRes And 'CNat 'CNat = 'CNat evalOp _ (CvNat i) (CvNat j) = Right $ CvNat (i .&. j)
src/Michelson/Typed/CValue.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE TemplateHaskell #-} -- | Module, containing CValue data type -- which represents Michelson comparable values. @@ -10,6 +11,7 @@ import Michelson.Typed.T (CT(..)) import Tezos.Core (Mutez, Timestamp) import Tezos.Crypto (KeyHash)+import Util.TH -- | Representation of comparable value -- in Michelson language.@@ -21,7 +23,7 @@ -- Only these values can be used as map keys -- or set elements. data CValue t where- CvInt :: Integer -> CValue 'CInt+ CvInt :: Integer -> CValue 'CInt CvNat :: Natural -> CValue 'CNat CvString :: MText -> CValue 'CString CvBytes :: ByteString -> CValue 'CBytes@@ -34,3 +36,5 @@ deriving stock instance Show (CValue t) deriving stock instance Eq (CValue t) deriving stock instance Ord (CValue t)++$(deriveGADTNFData ''CValue)
src/Michelson/Typed/Convert.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Michelson.Typed.Convert- ( convertContract+ ( convertContractCode , convertFullContract , instrToOps , untypeValue@@ -27,10 +27,10 @@ import Util.Peano import Util.Typeable -convertContract+convertContractCode :: forall param store . (SingI param, SingI store)- => Contract param store -> U.Contract-convertContract contract =+ => ContractCode param store -> U.Contract+convertContractCode contract = U.Contract { para = untypeDemoteT @param , stor = untypeDemoteT @store@@ -44,9 +44,9 @@ :: forall param store . (SingI param, SingI store) => FullContract param store -> U.Contract convertFullContract fc =- let c = convertContract (fcCode fc)- in c { U.para = mkUType sing (fcParamNotes fc)- , U.stor = mkUType sing (fcStoreNotes fc) }+ let c = convertContractCode (fcCode fc)+ in c { U.para = mkUType (fcParamNotes fc)+ , U.stor = mkUType (fcStoreNotes fc) } -- | Convert a typed 'Val' to an untyped 'Value'. --@@ -161,37 +161,37 @@ handleInstrAnnotate :: forall inp' out' . HasCallStack => Instr inp' out' -> PackedNotes out' -> U.ExpandedInstr- handleInstrAnnotate ins' (PackedNotes notes' sing') = let- x = handleInstr ins' in addInstrNote x sing' notes'+ handleInstrAnnotate ins' (PackedNotes notes') = let+ x = handleInstr ins' in addInstrNote x notes' where addInstrNote- :: HasCallStack- => U.ExpandedInstr -> Sing t -> Notes t -> U.ExpandedInstr- addInstrNote ins s nte = case (s, ins, nte) of- (_, U.PUSH va _ v, _) -> U.PUSH va (mkUType s nte) v+ :: forall t. (SingI t, HasCallStack)+ => U.ExpandedInstr -> Notes t -> U.ExpandedInstr+ addInstrNote ins nte = case (sing @t, ins, nte) of+ (_, U.PUSH va _ v, _) -> U.PUSH va (mkUType nte) v (_, U.SOME _ va, NTOption ta _) -> U.SOME ta va- (STOption sp, U.NONE _ va _, NTOption ta nt) -> U.NONE ta va (mkUType sp nt)+ (STOption _, U.NONE _ va _, NTOption ta nt) -> U.NONE ta va (mkUType nt) (_, U.UNIT _ va, NTUnit ta) -> U.UNIT ta va (_, U.PAIR _ va _ _, NTPair ta f1 f2 _ _) -> U.PAIR ta va f1 f2 (_, U.CAR va f1, _) -> U.CAR va f1 (_, U.CDR va f1, _) -> U.CDR va f1- (STOr _ sq, U.LEFT _ va _ _ _, NTOr ta f1 f2 _ n2) ->- U.LEFT ta va f1 f2 (mkUType sq n2)- (STOr sp _, U.RIGHT _ va _ _ _, NTOr ta f1 f2 n1 _) ->- U.RIGHT ta va f1 f2 (mkUType sp n1)- (STList sp, U.NIL _ va _, NTList ta n) -> U.NIL ta va (mkUType sp n)+ (STOr _ _, U.LEFT _ va _ _ _, NTOr ta f1 f2 _ n2) ->+ U.LEFT ta va f1 f2 (mkUType n2)+ (STOr _ _, U.RIGHT _ va _ _ _, NTOr ta f1 f2 n1 _) ->+ U.RIGHT ta va f1 f2 (mkUType n1)+ (STList _, U.NIL _ va _, NTList ta n) -> U.NIL ta va (mkUType n) (_, U.EMPTY_SET _ va ct, NTSet ta1 ta2) -> U.EMPTY_SET ta1 va (addCtNote ta2 ct)- (STMap _ sq, U.EMPTY_MAP _ va ct _, NTMap ta1 ta2 n) ->- U.EMPTY_MAP ta1 va (addCtNote ta2 ct) (mkUType sq n)- (STBigMap _ sq, U.EMPTY_BIG_MAP _ va ct _, NTBigMap ta1 ta2 n) ->- U.EMPTY_BIG_MAP ta1 va (addCtNote ta2 ct) (mkUType sq n)- (STLambda sp sq, U.LAMBDA va _ _ ops, NTLambda _ n1 n2) ->- U.LAMBDA va (mkUType sp n1) (mkUType sq n2) ops- (_, U.CAST va _, n) -> U.CAST va (mkUType s n)- (STOption sp, U.UNPACK _ va _, NTOption ta nt) -> U.UNPACK ta va (mkUType sp nt)- (STOption (STContract sp), U.CONTRACT va fa _, NTOption _ (NTContract _ nt)) ->- U.CONTRACT va fa (mkUType sp nt)+ (STMap _ _, U.EMPTY_MAP _ va ct _, NTMap ta1 ta2 n) ->+ U.EMPTY_MAP ta1 va (addCtNote ta2 ct) (mkUType n)+ (STBigMap _ _, U.EMPTY_BIG_MAP _ va ct _, NTBigMap ta1 ta2 n) ->+ U.EMPTY_BIG_MAP ta1 va (addCtNote ta2 ct) (mkUType n)+ (STLambda _ _, U.LAMBDA va _ _ ops, NTLambda _ n1 n2) ->+ U.LAMBDA va (mkUType n1) (mkUType n2) ops+ (_, U.CAST va _, n) -> U.CAST va (mkUType n)+ (STOption _, U.UNPACK _ va _, NTOption ta nt) -> U.UNPACK ta va (mkUType nt)+ (STOption (STContract _), U.CONTRACT va fa _, NTOption _ (NTContract _ nt)) ->+ U.CONTRACT va fa (mkUType nt) (_, U.CONTRACT va fa t, NTOption _ _) -> U.CONTRACT va fa t (_, a@(U.APPLY {}), _) -> a (_, a@(U.CHAIN_ID {}), _) -> a@@ -446,7 +446,7 @@ i@(CONTRACT nt epName) | _ :: Instr ('Tc 'CAddress ': s) ('TOption ('TContract p) ': s) <- i -> let fa = epNameToRefAnn epName- in U.CONTRACT U.noAnn fa (mkUType (sing @p) nt)+ in U.CONTRACT U.noAnn fa (mkUType nt) TRANSFER_TOKENS -> U.TRANSFER_TOKENS U.noAnn SET_DELEGATE -> U.SET_DELEGATE U.noAnn i@(CREATE_CONTRACT fullContract)
src/Michelson/Typed/Doc.hs view
@@ -1,8 +1,11 @@ -- | Extracting documentation from instructions set. module Michelson.Typed.Doc ( buildInstrDoc+ , buildInstrDocWithGitRev , modifyInstrDoc+ , modifyInstrAllDoc , cutInstrNonDoc+ , docInstr ) where import Control.Lens (at)@@ -41,6 +44,20 @@ Just () -> pass Nothing -> someDocItemToContractDoc (SomeDocItem dep) +-- | Put a document item.+docInstr :: DocItem di => di -> Instr s s+docInstr = Ext . DOC_ITEM . SomeDocItem++attachGitInfo :: DGitRevision -> Instr inp out -> Instr inp out+attachGitInfo gitRev = modifyInstrDoc $ \case+ DGitRevisionUnknown -> Just gitRev+ _ -> Nothing++-- | Assemble contract documentation with the revision of the contract.+buildInstrDocWithGitRev :: DGitRevision -> Instr inp out -> ContractDoc+buildInstrDocWithGitRev gitRev contract =+ buildInstrDoc $ attachGitInfo gitRev contract+ -- | Assemble contract documentation. buildInstrDoc :: Instr inp out -> ContractDoc buildInstrDoc = dfsFoldInstr dfsSettings $ \case@@ -75,14 +92,19 @@ -- | Recursevly traverse an instruction and modify documentation items -- matching given type.+--+-- If mapper returns 'Nothing', doc item will remain unmodified. modifyInstrDoc- :: DocItem i- => (i -> i)+ :: (DocItem i1, DocItem i2)+ => (i1 -> Maybe i2) -> Instr inp out -> Instr inp out modifyInstrDoc mapper = modifyInstrAllDoc untypedMapper where- untypedMapper sdi@(SomeDocItem di) = maybe sdi (SomeDocItem . mapper) (cast di)+ untypedMapper sdi@(SomeDocItem di) = fromMaybe sdi $ do+ di' <- cast di+ newDi <- mapper di'+ return (SomeDocItem newDi) -- | Leave only instructions related to documentation. --
src/Michelson/Typed/EntryPoints.hs view
@@ -9,6 +9,7 @@ , ParamNotes (..) , pattern ParamNotes+ , starParamNotes , ArmCoord (..) , ArmCoords , ParamEpError (..)@@ -27,6 +28,7 @@ , mkEntryPointCall , tyImplicitAccountParam+ , flattenEntryPoints -- * Re-exports , EpName (..)@@ -41,7 +43,7 @@ import Data.Constraint (Dict(..)) import Data.Default (Default(..)) import qualified Data.List.NonEmpty as NE-import Data.Singletons (Sing, SingI(..))+import Data.Singletons (SingI(..)) import qualified Data.Text as T import Data.Typeable ((:~:)(..)) import Fmt (Buildable(..), pretty, (+|), (|+))@@ -49,19 +51,22 @@ import Michelson.Text import Michelson.Typed.Annotation+import Michelson.Typed.Extract import Michelson.Typed.Scope import Michelson.Typed.Sing import Michelson.Typed.T+import qualified Michelson.Untyped as U import Michelson.Untyped.Annotation import Michelson.Untyped.EntryPoints import Tezos.Address (Address, ParseAddressError, formatAddress, parseAddress)+import Util.TH import Util.Typeable import Util.TypeLits ---------------------------------------------------------------------------- -- Primitives -----------------------------------------------------------------------------+-- -- EpAddress ---------------------------------------------------------------------------- @@ -72,11 +77,13 @@ -- ^ Address itself , eaEntryPoint :: EpName -- ^ Entrypoint name (might be empty)- } deriving stock (Show, Eq, Ord)+ } deriving stock (Show, Eq, Ord, Generic) instance Buildable EpAddress where build = build . formatEpAddress +instance NFData EpAddress+ formatEpAddress :: EpAddress -> Text formatEpAddress (EpAddress addr ep) | ep == def = formatAddress addr@@ -93,8 +100,10 @@ = ParseEpAddressBadAddress ParseAddressError | ParseEpAddressBadRefAnn Text | ParseEpAddressRefAnnError EpNameFromRefAnnError- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData ParseEpAddressError+ instance Buildable ParseEpAddressError where build = \case ParseEpAddressBadAddress err -> build err@@ -134,17 +143,25 @@ -- 2. If @default@ entrypoint is explicitly assigned, no "arm" remains uncallable. newtype ParamNotes (t :: T) = ParamNotesUnsafe { unParamNotes :: Notes t- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData (ParamNotes t)+ pattern ParamNotes :: Notes t -> ParamNotes t pattern ParamNotes t <- ParamNotesUnsafe t {-# COMPLETE ParamNotes #-} +-- | Parameter without annotations.+starParamNotes :: SingI t => ParamNotes t+starParamNotes = ParamNotesUnsafe starNotes+ -- | Coordinates of "arm" in Or tree, used solely in error messages. type ArmCoords = [ArmCoord] data ArmCoord = AcLeft | AcRight- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData ArmCoord+ instance Buildable ArmCoord where build = \case AcLeft -> "left"@@ -154,8 +171,10 @@ data ParamEpError = ParamEpDuplicatedNames (NonEmpty EpName) | ParamEpUncallableArm ArmCoords- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData ParamEpError+ instance Buildable ParamEpError where build = \case ParamEpDuplicatedNames names -> mconcat@@ -252,6 +271,8 @@ deriving stock instance Eq (EpLiftSequence arg param) deriving stock instance Show (EpLiftSequence arg param) +$(deriveGADTNFData ''EpLiftSequence)+ instance Buildable (EpLiftSequence arg param) where build = \case EplArgHere -> "×"@@ -271,6 +292,8 @@ deriving stock instance Eq (EntryPointCallT param arg) deriving stock instance Show (EntryPointCallT param arg)+instance NFData (EntryPointCallT param arg) where+ rnf (EntryPointCall name Proxy s) = rnf (name, s) instance Buildable (EntryPointCallT param arg) where build EntryPointCall{..} =@@ -318,6 +341,8 @@ guard (epc1 == epc2) deriving stock instance Show (SomeEntryPointCallT arg)+instance NFData (SomeEntryPointCallT arg) where+ rnf (SomeEpc epc) = rnf epc instance Buildable (SomeEntryPointCallT arg) where build (SomeEpc epc) = build epc@@ -347,20 +372,21 @@ -- Returns 'Nothing' if entrypoint is not found. -- Does not treat default entrypoints specially. withEpLiftSequence- :: (ParameterScope param)+ :: forall param r.+ (ParameterScope param) => EpName- -> (Sing param, Notes param)+ -> Notes param -> (forall arg. (ParameterScope arg) => (Notes arg, EpLiftSequence arg param) -> r) -> Maybe r withEpLiftSequence epName@(epNameToParamAnn -> epAnn) param cont =- case param of- (STOr lSing rSing, NTOr _ lFieldAnn rFieldAnn lNotes rNotes) ->+ case (sing @param, param) of+ (STOr lSing _, NTOr _ lFieldAnn rFieldAnn lNotes rNotes) -> case (checkOpPresence lSing, checkNestedBigMapsPresence lSing) of (OpAbsent, NestedBigMapsAbsent) -> asum [ guard (lFieldAnn == epAnn) $> cont (lNotes, EplWrapLeft EplArgHere) , guard (rFieldAnn == epAnn) $> cont (rNotes, EplWrapRight EplArgHere)- , withEpLiftSequence epName (lSing, lNotes) (cont . fmap @((,) _) EplWrapLeft)- , withEpLiftSequence epName (rSing, rNotes) (cont . fmap @((,) _) EplWrapRight)+ , withEpLiftSequence epName lNotes (cont . fmap @((,) _) EplWrapLeft)+ , withEpLiftSequence epName rNotes (cont . fmap @((,) _) EplWrapRight) ] _ -> Nothing @@ -381,11 +407,11 @@ mkEntryPointCall :: (ParameterScope param) => EpName- -> (Sing param, ParamNotes param)+ -> ParamNotes param -> Maybe (MkEntryPointCallRes param)-mkEntryPointCall epName (paramSing, ParamNotes paramNotes) =+mkEntryPointCall epName (ParamNotes paramNotes) = asum- [ withEpLiftSequence epName (paramSing, paramNotes) $ \(argInfo, liftSeq) ->+ [ withEpLiftSequence epName paramNotes $ \(argInfo, liftSeq) -> MkEntryPointCallRes argInfo $ EntryPointCall { epcName = epName , epcParamProxy = Proxy@@ -400,8 +426,17 @@ ] -- | "Parameter" type of implicit account.-tyImplicitAccountParam :: (Sing 'TUnit, ParamNotes 'TUnit)-tyImplicitAccountParam = (sing, ParamNotesUnsafe starNotes)+tyImplicitAccountParam :: ParamNotes 'TUnit+tyImplicitAccountParam = ParamNotesUnsafe starNotes++-- Misc+----------------------------------------------------------------------------++-- | Flatten a provided list of notes to a map of its entrypoints and its+-- corresponding utype. Please refer to 'mkEntrypointsMap' in regards to how+-- duplicate entrypoints are handled.+flattenEntryPoints :: SingI t => ParamNotes t -> Map EpName U.Type+flattenEntryPoints (unParamNotes -> notes) = mkEntrypointsMap (mkUType notes) -- TODO [#35]: We also need to be able to handle field annotation in root -- of parameter's @or@ tree.
src/Michelson/Typed/Extract.hs view
@@ -12,11 +12,12 @@ , withUType , pattern AsUType+ , pattern AsUTypeExt ) where -import Data.Singletons (SingI, sing)+import Data.Singletons (SingI) -import Michelson.Typed.Annotation (Notes(..))+import Michelson.Typed.Annotation (Notes(..), notesSing) import Michelson.Typed.Sing (Sing(..), fromSingCT, fromSingT, withSomeSingCT) import Michelson.Typed.T (T(..), toUType) import qualified Michelson.Untyped as Un@@ -24,104 +25,104 @@ {-# ANN module ("HLint: ignore Avoid lambda using `infix`" :: Text) #-} fromUType :: Un.Type -> T-fromUType ut = withUType ut (fromSingT . fst)+fromUType ut = withUType ut (fromSingT . notesSing) -mkUType :: Sing x -> Notes x -> Un.Type-mkUType s notes = case (s, notes) of+mkUType :: SingI x => Notes x -> Un.Type+mkUType notes = case (notesSing notes, notes) of (STc ct, NTc tn) -> Un.Type (Un.Tc (fromSingCT ct)) tn (STKey, NTKey tn) -> Un.Type Un.TKey tn (STUnit, NTUnit tn) -> Un.Type Un.TUnit tn (STSignature, NTSignature tn) -> Un.Type Un.TSignature tn (STChainId, NTChainId tn) -> Un.Type Un.TChainId tn- (STOption t, NTOption tn n) -> Un.Type (Un.TOption (mkUType t n)) tn- (STList t, NTList tn n) -> Un.Type (Un.TList (mkUType t n)) tn+ (STOption _, NTOption tn n) -> Un.Type (Un.TOption (mkUType n)) tn+ (STList _, NTList tn n) -> Un.Type (Un.TList (mkUType n)) tn (STSet ct, NTSet tn n) -> Un.Type (Un.TSet $ mkComp ct n) tn (STOperation, NTOperation tn) -> Un.Type Un.TOperation tn- (STContract t, NTContract tn n) ->- Un.Type (Un.TContract (mkUType t n)) tn- (STPair tl tr, NTPair tn fl fr nl nr) ->- Un.Type (Un.TPair fl fr (mkUType tl nl) (mkUType tr nr)) tn- (STOr tl tr, NTOr tn fl fr nl nr) ->- Un.Type (Un.TOr fl fr (mkUType tl nl) (mkUType tr nr)) tn- (STLambda p q, NTLambda tn np nq) ->- Un.Type (Un.TLambda (mkUType p np) (mkUType q nq)) tn- (STMap k v, NTMap tn nk nv) ->- Un.Type (Un.TMap (mkComp k nk) (mkUType v nv)) tn- (STBigMap k v, NTBigMap tn nk nv) ->- Un.Type (Un.TBigMap (mkComp k nk) (mkUType v nv)) tn+ (STContract _, NTContract tn n) ->+ Un.Type (Un.TContract (mkUType n)) tn+ (STPair _ _, NTPair tn fl fr nl nr) ->+ Un.Type (Un.TPair fl fr (mkUType nl) (mkUType nr)) tn+ (STOr _ _, NTOr tn fl fr nl nr) ->+ Un.Type (Un.TOr fl fr (mkUType nl) (mkUType nr)) tn+ (STLambda _ _, NTLambda tn np nq) ->+ Un.Type (Un.TLambda (mkUType np) (mkUType nq)) tn+ (STMap k _, NTMap tn nk nv) ->+ Un.Type (Un.TMap (mkComp k nk) (mkUType nv)) tn+ (STBigMap k _, NTBigMap tn nk nv) ->+ Un.Type (Un.TBigMap (mkComp k nk) (mkUType nv)) tn where mkComp t a = Un.Comparable (fromSingCT t) a -- | Convert 'Un.Type' to the isomorphic set of information from typed world. withUType :: Un.Type- -> (forall t. (Typeable t, SingI t) => (Sing t, Notes t) -> r)+ -> (forall t. (Typeable t, SingI t) => Notes t -> r) -> r withUType (Un.Type ut tn) cont = case ut of Un.Tc tc -> withSomeSingCT tc $- \(_ :: Sing tc) -> cont (sing @('Tc tc), NTc tn)+ \(_ :: Sing tc) -> cont @('Tc tc) (NTc tn) Un.TKey ->- cont (sing @'TKey, NTKey tn)+ cont (NTKey tn) Un.TUnit ->- cont (sing @'TUnit, NTUnit tn)+ cont (NTUnit tn) Un.TSignature ->- cont (sing @'TSignature, NTSignature tn)+ cont (NTSignature tn) Un.TChainId ->- cont (sing @'TChainId, NTChainId tn)+ cont (NTChainId tn) Un.TOption internalT -> withUType internalT $- \(_ :: Sing internalT, notesInternalT :: Notes internalT) ->- cont (sing @('TOption internalT), NTOption tn notesInternalT)+ \(notesInternalT :: Notes internalT) ->+ cont (NTOption tn notesInternalT) Un.TList listT -> withUType listT $- \(_ :: Sing listT, notesListT :: Notes listT) ->- cont (sing @('TList listT), NTList tn notesListT)+ \(notesListT :: Notes listT) ->+ cont (NTList tn notesListT) Un.TSet (Un.Comparable sCT nsCT) -> withSomeSingCT sCT $- \(_ :: Sing sCT) -> cont (sing @('TSet sCT), NTSet tn nsCT)+ \(_ :: Sing sCT) -> cont @('TSet sCT) (NTSet tn nsCT) Un.TOperation ->- cont (sing @'TOperation, NTOperation tn)+ cont (NTOperation tn) Un.TContract contractT -> withUType contractT $- \(_ :: Sing contractT, notesContractT :: Notes contractT) ->- cont (sing @('TContract contractT), NTContract tn notesContractT)+ \(notesContractT :: Notes contractT) ->+ cont (NTContract tn notesContractT) Un.TPair la ra lt rt ->- withUType lt $ \(_ :: Sing lt, ln :: Notes lt) ->- withUType rt $ \(_ :: Sing rt, rn :: Notes rt) ->- cont (sing @('TPair lt rt), NTPair tn la ra ln rn)+ withUType lt $ \ln ->+ withUType rt $ \rn ->+ cont (NTPair tn la ra ln rn) Un.TOr la ra lt rt ->- withUType lt $ \(_ :: Sing lt, ln :: Notes lt) ->- withUType rt $ \(_ :: Sing rt, rn :: Notes rt) ->- cont (sing @('TOr lt rt), NTOr tn la ra ln rn)+ withUType lt $ \ln ->+ withUType rt $ \rn ->+ cont (NTOr tn la ra ln rn) Un.TLambda lt rt ->- withUType lt $ \(_ :: Sing lt, ln :: Notes lt) ->- withUType rt $ \(_ :: Sing rt, rn :: Notes rt) ->- cont (sing @('TLambda lt rt), NTLambda tn ln rn)+ withUType lt $ \ln ->+ withUType rt $ \rn ->+ cont (NTLambda tn ln rn) Un.TMap (Un.Comparable keyT notesT) valT -> withSomeSingCT keyT $ \(_ :: Sing keyT) ->- withUType valT $ \(_ :: Sing valT, valN :: Notes valt) ->- cont (sing @('TMap keyT valT), NTMap tn notesT valN)+ withUType valT $ \valN ->+ cont @('TMap keyT _) (NTMap tn notesT valN) Un.TBigMap (Un.Comparable keyT notesT) valT -> withSomeSingCT keyT $ \(_ :: Sing keyT) ->- withUType valT $ \(_ :: Sing valT, valN :: Notes valt) ->- cont (sing @('TBigMap keyT valT), NTBigMap tn notesT valN)+ withUType valT $ \valN ->+ cont @('TBigMap keyT _) (NTBigMap tn notesT valN) -- Helper datatype for 'AsUType'.-data SomeTypedInfo = forall t. (Typeable t, SingI t) => SomeTypedInfo (Sing t, Notes t)+data SomeTypedInfo = forall t. (Typeable t, SingI t) => SomeTypedInfo (Notes t) --- | Transparently represent untyped 'Type' as wrapper over @(Sing t, Notes t)@--- from typed world.+-- | Transparently represent untyped 'Type' as wrapper over @Notes t@+-- from typed world with @SingI t@ constraint. -- -- As expression this carries logic of 'mkUType', and as pattern it performs -- 'withUType' but may make code a bit cleaner.@@ -129,7 +130,13 @@ -- Note about constraints: pattern signatures usually require two constraints - -- one they require and another one which they provide. In our case we require -- nothing (thus first constraint is @()@) and provide some knowledge about @t@.-pattern AsUType :: () => (Typeable t, SingI t) => Sing t -> Notes t -> Un.Type-pattern AsUType sng notes <- ((\ut -> withUType ut SomeTypedInfo) -> SomeTypedInfo (sng, notes))- where AsUType sng notes = mkUType sng notes+pattern AsUType :: () => (Typeable t, SingI t) => Notes t -> Un.Type+pattern AsUType notes <- ((\ut -> withUType ut SomeTypedInfo) -> SomeTypedInfo notes)+ where AsUType notes = mkUType notes {-# COMPLETE AsUType #-}++-- | Similar to 'AsUType', but also gives 'Sing' for given type.+pattern AsUTypeExt :: () => (Typeable t, SingI t) => Sing t -> Notes t -> Un.Type+pattern AsUTypeExt sng notes <- AsUType notes@(notesSing -> sng)+ where AsUTypeExt _ notes = AsUType notes+{-# COMPLETE AsUTypeExt #-}
src/Michelson/Typed/Haskell/Doc.hs view
@@ -305,9 +305,10 @@ -- | Doc element with description of contract storage type. newtype DStorageType = DStorageType DType+ deriving stock Generic instance DocItem DStorageType where- type DocItemPosition DStorageType = 2035+ type DocItemPosition DStorageType = 835 docItemSectionName = Just "Storage" docItemToMarkdown lvl (DStorageType t) = docItemToMarkdown lvl t docItemDependencies (DStorageType t) = docItemDependencies t@@ -334,7 +335,7 @@ -- for the whole type) and zero or more type arguments. customTypeDocMdReference :: (Text, DType) -> [DType] -> WithinParens -> Markdown customTypeDocMdReference (typeCtorName, tyDoc) typeArgsDoc wp =- let DocItemRef (DocItemId ctorDocItemId) = docItemRef tyDoc+ let DocItemRef ctorDocItemId = docItemRef tyDoc in applyWithinParens wpSmart $ mconcat . intersperse " " $ ( mdLocalRef (mdTicked $ build typeCtorName) ctorDocItemId@@ -569,9 +570,9 @@ typeDocName _ = "Text" typeDocMdReference p = customTypeDocMdReference ("Text", DType p) [] typeDocMdDescription =- "Michelson string.\n\- \Not every text literal is valid string, see list of constraints in the \- \[Official Michelson documentation](https://tezos.gitlab.io/whitedoc/michelson.html#constants)."+ "Michelson string.\n\n\+ \This has to contain only ASCII characters with codes from [32; 126] range; \+ \additionally, newline feed character is allowed." typeDocDependencies _ = [] typeDocHaskellRep _ = Nothing
src/Michelson/Typed/Haskell/Instr/Product.hs view
@@ -18,7 +18,6 @@ import Data.Type.Bool (If) import Data.Type.Equality (type (==)) import Data.Vinyl.Core (Rec(..))-import Data.Vinyl.Derived (Label) import Data.Vinyl.TypeLevel (type (++)) import GHC.Generics ((:*:)(..), (:+:)(..)) import qualified GHC.Generics as G@@ -29,6 +28,7 @@ import Michelson.Typed.Haskell.Value import Michelson.Typed.Instr import Michelson.Text+import Util.Label (Label) import Util.Named (NamedInner) import Util.Type
src/Michelson/Typed/Haskell/Instr/Sum.hs view
@@ -40,7 +40,6 @@ import Data.Type.Bool (If, type (||)) import Data.Type.Equality (type (==)) import Data.Vinyl.Core (Rec(..))-import Data.Vinyl.Derived (Label) import Data.Vinyl.TypeLevel (type (++)) import GHC.Generics ((:*:)(..), (:+:)(..)) import qualified GHC.Generics as G@@ -59,6 +58,7 @@ import Tezos.Address (Address) import Tezos.Core (Mutez, Timestamp) import Tezos.Crypto (KeyHash, PublicKey, Signature)+import Util.Label (Label) import Util.Type import Util.TypeTuple @@ -467,11 +467,11 @@ -- | Pattern-match on the given datatype. instrCase :: forall dt out inp.- InstrCaseC dt inp out+ InstrCaseC dt => Rec (CaseClause inp out) (CaseClauses dt) -> RemFail Instr (ToT dt ': inp) out instrCase = gInstrCase @(G.Rep dt) -type InstrCaseC dt inp out = (GenericIsoValue dt, GInstrCase (G.Rep dt))+type InstrCaseC dt = (GenericIsoValue dt, GInstrCase (G.Rep dt)) -- | In what different case branches differ - related constructor name and -- input stack type which the branch starts with.
src/Michelson/Typed/Haskell/Value.hs view
@@ -26,9 +26,9 @@ , totsAppendLemma ) where -import Data.Default (Default (..))+import Data.Constraint ((:-)(..), Dict(..))+import Data.Default (Default(..)) import qualified Data.Kind as Kind-import Data.Constraint (Dict (..), (:-)(..)) import qualified Data.Map.Strict as Map import qualified Data.Set as Set import Data.Vinyl.Core (Rec(..))@@ -36,6 +36,7 @@ import GHC.Generics ((:*:)(..), (:+:)(..)) import qualified GHC.Generics as G import Named (NamedF(..))+import Test.QuickCheck (Arbitrary(..)) import Michelson.Text import Michelson.Typed.Aliases@@ -44,7 +45,7 @@ import Michelson.Typed.T import Michelson.Typed.Value import Tezos.Address (Address)-import Tezos.Core (Mutez, Timestamp, ChainId)+import Tezos.Core (ChainId, Mutez, Timestamp) import Tezos.Crypto (KeyHash, PublicKey, Signature) import Util.Type @@ -329,6 +330,9 @@ type ToT (BigMap k v) = 'TBigMap (ToCT k) (ToT v) toVal = VBigMap . Map.mapKeys toCVal . Map.map toVal . unBigMap fromVal (VBigMap x) = BigMap $ Map.map fromVal $ Map.mapKeys fromCVal x++instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (BigMap k v) where+ arbitrary = BigMap <$> arbitrary -- Generic magic ----------------------------------------------------------------------------
src/Michelson/Typed/Instr.hs view
@@ -9,12 +9,13 @@ , mkStackRef , PrintComment (..) , TestAssert (..)- , Contract+ , ContractCode , FullContract (..) , fcParamNotes , mapFullContractCode , pattern CAR , pattern CDR+ , pattern UNPAIR , PackedNotes(..) , ConstraintDIPN , ConstraintDIPN'@@ -25,13 +26,14 @@ ) where import Data.Singletons (SingI(..))-import Fmt ((+||), (||+))+import Fmt (Buildable(..), (+||), (||+)) import qualified GHC.TypeNats as GHC (Nat) import qualified Text.Show +import Michelson.Doc+import Michelson.Printer.Util (RenderDoc(..), buildRenderDoc, needsParens, printDocS) import Michelson.Typed.Annotation (Notes(..)) import Michelson.Typed.Arith-import Michelson.Doc import Michelson.Typed.EntryPoints import Michelson.Typed.Polymorphic import Michelson.Typed.Scope@@ -39,6 +41,7 @@ import Michelson.Typed.Value (ContractInp, ContractOut, Value'(..)) import Michelson.Untyped (Annotation(..), FieldAnn, VarAnn) import Util.Peano+import Util.TH import Util.Type (type (++), KnownList) -- | A wrapper to wrap annotations and corresponding singleton.@@ -47,11 +50,20 @@ -- the `Show` instance for `Instr` as `Sing a` does not have a `Show` -- instance on its own. data PackedNotes a where- PackedNotes :: Notes a -> Sing a -> PackedNotes (a ': s)+ PackedNotes :: SingI a => Notes a -> PackedNotes (a ': s) +instance NFData (PackedNotes a) where+ rnf (PackedNotes n) = rnf n+ instance Show (PackedNotes a) where- show (PackedNotes n _) = show n+ show = printDocS True . renderDoc needsParens +instance Buildable (PackedNotes a) where+ build = buildRenderDoc++instance RenderDoc (PackedNotes a) where+ renderDoc pn (PackedNotes n) = renderDoc pn n+ -- | Constraint that is used in DIPN, we want to share it with -- typechecking code and eDSL code. type ConstraintDIPN' kind (n :: Peano) (inp :: [kind])@@ -159,15 +171,15 @@ DROP :: Instr (a ': s) s DROPN :: forall (n :: Peano) s.- (SingI n, KnownPeano n, RequireLongerOrSameLength s n)+ (SingI n, KnownPeano n, RequireLongerOrSameLength s n, NFData (Sing n)) => Sing n -> Instr s (Drop n s) DUP :: Instr (a ': s) (a ': a ': s) SWAP :: Instr (a ': b ': s) (b ': a ': s) DIG- :: forall (n :: Peano) inp out a. ConstraintDIG n inp out a+ :: forall (n :: Peano) inp out a. (ConstraintDIG n inp out a, NFData (Sing n)) => Sing n -> Instr inp out DUG- :: forall (n :: Peano) inp out a. ConstraintDUG n inp out a+ :: forall (n :: Peano) inp out a. (ConstraintDUG n inp out a, NFData (Sing n)) => Sing n -> Instr inp out PUSH :: forall t s . ConstantScope t@@ -223,7 +235,7 @@ => Instr (a ': 'TLambda ('TPair a b) c ': s) ('TLambda b c ': s) DIP :: Instr a c -> Instr (b ': a) (b ': c) DIPN- :: forall (n :: Peano) inp out s s'. ConstraintDIPN n inp out s s'+ :: forall (n :: Peano) inp out s s'. (ConstraintDIPN n inp out s s', (NFData (Sing n))) => Sing n -> Instr s s' -> Instr inp out FAILWITH :: (Typeable a, SingI a) => Instr (a ': s) t CAST :: forall a s . SingI a => Instr (a ': s) (a ': s)@@ -345,19 +357,23 @@ instance Monoid (Instr s s) where mempty = Nop -instance NFData (Instr inp out) where- -- This is the simplest way, and we don't care much about performance in tests.- -- May be unsatisfactory for benchmarks though.- -- If @instance Show@ is not automatically derived anymore, or you have other- -- problems with this instance, then please reimplement it manually.- rnf = rnf @String . show--pattern CAR :: Instr ('TPair a b : s) (a : s)+-- We have to write down pattern like this because simple+-- @Instr (TPair a b : s) (a : s)@ signature would assume that we /expect/+-- the input stack to have pair on top, but we want to /provide/ this info+-- in scope of a pattern-match.+-- In pattern declaration we have to write down the two mentioned constraints+-- explicitly.+--+-- Note that internally GADT constructors are rewritten in the very same way.+pattern CAR :: () => (i ~ ('TPair a b : s), o ~ (a : s)) => Instr i o pattern CAR = AnnCAR (AnnotationUnsafe "") -pattern CDR :: Instr ('TPair a b : s) (b: s)+pattern CDR :: () => (i ~ ('TPair a b : s), o ~ (b : s)) => Instr i o pattern CDR = AnnCDR (AnnotationUnsafe "") +pattern UNPAIR :: () => (i ~ ('TPair a b : s), o ~ (a : b : s)) => Instr i o+pattern UNPAIR = Seq DUP (Seq CAR (DIP CDR))+ data TestAssert (s :: [T]) where TestAssert :: (Typeable out)@@ -367,6 +383,8 @@ -> TestAssert inp deriving stock instance Show (TestAssert s)+instance NFData (TestAssert s) where+ rnf (TestAssert a b c) = rnf (a, b, c) -- | A reference into the stack of a given type. data StackRef (st :: [T]) where@@ -375,6 +393,9 @@ :: (KnownPeano idx, SingI idx, RequireLongerThan st idx) => Sing (idx :: Peano) -> StackRef st +instance NFData (StackRef st) where+ rnf (StackRef s) = rnf s+ instance Eq (StackRef st) where StackRef snat1 == StackRef snat2 = peanoVal snat1 == peanoVal snat2 @@ -394,6 +415,8 @@ } deriving stock (Eq, Show, Generic) deriving newtype (Semigroup, Monoid) +instance NFData (PrintComment st)+ instance IsString (PrintComment st) where fromString = PrintComment . one . Left . fromString @@ -401,28 +424,35 @@ = TEST_ASSERT (TestAssert s) | PRINT (PrintComment s) | DOC_ITEM SomeDocItem- deriving stock (Show)+ deriving stock (Show, Generic) +instance NFData (ExtInstr s)+ --------------------------------------------------- -type Contract cp st = Instr (ContractInp cp st) (ContractOut st)+type ContractCode cp st = Instr (ContractInp cp st) (ContractOut st) -- | Typed contract and information about annotations -- which is not present in the contract code.--- TODO [#12]: rename this to 'Contract' and the current 'Contract' to 'ContractCode'?+-- TODO [#12]: rename this to 'Contract' data FullContract cp st = (ParameterScope cp, StorageScope st) => FullContract- { fcCode :: Contract cp st+ { fcCode :: ContractCode cp st , fcParamNotesSafe :: ParamNotes cp , fcStoreNotes :: Notes st } deriving stock instance Show (FullContract cp st)+deriving stock instance Eq (ContractCode cp st) => Eq (FullContract cp st)+instance NFData (FullContract cp st) where+ rnf (FullContract a b c) = rnf (a, b, c) fcParamNotes :: FullContract cp st -> Notes cp fcParamNotes = unParamNotes . fcParamNotesSafe mapFullContractCode- :: (Contract cp st -> Contract cp st)+ :: (ContractCode cp st -> ContractCode cp st) -> FullContract cp st -> FullContract cp st mapFullContractCode f contract = contract { fcCode = f $ fcCode contract }++$(deriveGADTNFData ''Instr)
src/Michelson/Typed/T.hs view
@@ -31,7 +31,9 @@ | TLambda T T | TMap CT T | TBigMap CT T- deriving stock (Eq, Show)+ deriving stock (Eq, Show, Generic)++instance NFData T -- | Converts from 'T' to 'Michelson.Type.Type'. toUType :: T -> Un.Type
src/Michelson/Typed/Value.hs view
@@ -22,6 +22,7 @@ , addressToVContract , buildVContract , compileEpLiftSequence+ , liftCallArg ) where import Data.Singletons (SingI)@@ -35,6 +36,7 @@ import Tezos.Address (Address) import Tezos.Core (ChainId, Mutez) import Tezos.Crypto (KeyHash, PublicKey, Signature)+import Util.TH import Util.Typeable -- | Data type, representing operation, list of which is returned@@ -49,6 +51,7 @@ OpSetDelegate :: SetDelegate -> Operation' instr OpCreateContract :: ( Show (instr (ContractInp cp st) (ContractOut st))+ , NFData (instr (ContractInp cp st) (ContractOut st)) , Typeable instr, ParameterScope cp, StorageScope st) => CreateContract instr cp st -> Operation' instr@@ -75,16 +78,20 @@ { ttTransferArgument :: Value' instr p , ttAmount :: Mutez , ttContract :: Value' instr ('TContract p)- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData (TransferTokens instr p)+ instance Buildable (TransferTokens instr p) where build TransferTokens {..} = "Transfer " +| ttAmount |+ " tokens to " +| buildVContract ttContract |+ "" data SetDelegate = SetDelegate { sdMbKeyHash :: Maybe KeyHash- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData SetDelegate+ instance Buildable SetDelegate where build (SetDelegate mbDelegate) = "Set delegate to " <> maybe "<nobody>" build mbDelegate@@ -101,6 +108,9 @@ , ccContractCode :: instr (ContractInp cp st) (ContractOut st) } +instance NFData (instr (ContractInp cp st) (ContractOut st)) => NFData (CreateContract instr cp st) where+ rnf (CreateContract a b c d e) = rnf (a, b, c, d, e)+ instance Buildable (CreateContract instr cp st) where build CreateContract {..} = "Create a new contract with" <>@@ -123,6 +133,9 @@ RfAlwaysFails :: (forall o'. instr i o') -> RemFail instr i o deriving stock instance (forall o'. Show (instr i o')) => Show (RemFail instr i o)+instance (forall o'. NFData (instr i o')) => NFData (RemFail instr i o) where+ rnf (RfNormal a) = rnf a+ rnf (RfAlwaysFails a) = rnf a -- | Ignoring distinction between constructors here, comparing only semantics. instance Eq (instr i o) => Eq (RemFail instr i o) where@@ -179,6 +192,7 @@ ( forall i o. ( Show (instr i o) , Eq (instr i o)+ , NFData (instr i o) ) ) => RemFail instr (inp ': '[]) (out ': '[]) -> Value' instr ('TLambda inp out)@@ -232,6 +246,13 @@ EplWrapLeft els -> VOr . Left . compileEpLiftSequence els EplWrapRight els -> VOr . Right . compileEpLiftSequence els +-- | Lift entrypoint argument to full parameter.+liftCallArg+ :: EntryPointCallT param arg+ -> Value' instr arg+ -> Value' instr param+liftCallArg epc = compileEpLiftSequence (epcLiftSequence epc)+ -- TODO: actually we should handle big maps with something close -- to following: --@@ -245,3 +266,7 @@ -- -- data BigMap op ref k v = BigMap -- { bmRef :: ref k v, bmChanges :: Map (CValue k) (Value'Op (Value' cp v)) }++$(deriveGADTNFData ''Operation')+$(deriveGADTNFData ''Value')+
src/Michelson/Untyped/Annotation.hs view
@@ -4,11 +4,24 @@ ( Annotation (..) , pattern Annotation , pattern WithAnn++ -- * Annotation Set+ , AnnotationSet+ , emptyAnnSet+ , fullAnnSet+ , isNoAnnSet+ , minAnnSetSize+ , singleAnnSet+ , singleGroupAnnSet++ -- * Rendering , KnownAnnTag(..) , TypeAnn , FieldAnn , VarAnn , SomeAnn++ -- * Creation and conversions , noAnn , ann , mkAnnotation@@ -16,8 +29,6 @@ , specialFieldAnn , isValidAnnStart , isValidAnnBodyChar- , renderAnn- , renderWEAnn , unifyAnn , ifAnnUnified , disjoinVn@@ -28,14 +39,16 @@ import Data.Char (isAlpha, isAscii, isNumber) import Data.Data (Data(..)) import Data.Default (Default(..))+import qualified Data.Kind as Kind import qualified Data.Text as T+import Data.Typeable ((:~:)(..), eqT) import Fmt (Buildable(build)) import Instances.TH.Lift () import Language.Haskell.TH.Lift (deriveLift)-import Text.PrettyPrint.Leijen.Text (Doc, textStrict)+import Text.PrettyPrint.Leijen.Text (Doc, (<+>), hsep, textStrict) import qualified Text.Show -import Michelson.Printer.Util (RenderDoc(..), buildRenderDoc)+import Michelson.Printer.Util (RenderDoc(..), buildRenderDoc, doesntNeedParens, printDocS) -- | Generic Type/Field/Variable Annotation --@@ -47,6 +60,8 @@ deriving stock (Eq, Data, Functor, Generic) deriving newtype (IsString) +instance NFData (Annotation tag)+ pattern Annotation :: Text -> Annotation tag pattern Annotation ann <- AnnotationUnsafe ann @@ -55,11 +70,92 @@ instance Default (Annotation tag) where def = noAnn -class KnownAnnTag tag where+--------------------------------------------------------------------------------+-- Annotation Set+--------------------------------------------------------------------------------++-- | An 'AnnotationSet' contains all the type/field/variable 'Annotation's+-- , with each group in order, associated with an entity.+-- Note that in its rendering/show instances the unnecessary annotations will be+-- omitted, as well as in some of the functions operating with it.+-- Necessary 'Annotation's are the ones strictly required for a consistent+-- representation.+-- In particular, for each group (t/f/v):+-- - if all annotations are 'noAnn' they are all omitted+-- - if one or more 'noAnn' follow a non-empty 'ann', they are omitted+-- - if one or more 'noAnn' precede a non-empty 'ann', they are kept+-- - every non-empty 'ann' is obviously kept+-- This is why order for each group is important as well as separation of+-- different groups of 'Annotation's.+data AnnotationSet = AnnotationSet+ { asTypes :: [TypeAnn]+ , asFields :: [FieldAnn]+ , asVars :: [VarAnn]+ } deriving Eq++instance Semigroup AnnotationSet where+ (AnnotationSet ts1 fs1 vs1) <> (AnnotationSet ts2 fs2 vs2) = AnnotationSet {..}+ where+ asTypes = ts1 <> ts2+ asFields = fs1 <> fs2+ asVars = vs1 <> vs2++instance Monoid AnnotationSet where+ mempty = emptyAnnSet++-- | An 'AnnotationSet' without any 'Annotation'.+emptyAnnSet :: AnnotationSet+emptyAnnSet = AnnotationSet [] [] []++-- | An 'AnnotationSet' with only a single 'Annotation' (of any kind).+singleAnnSet :: forall tag. KnownAnnTag tag => Annotation tag -> AnnotationSet+singleAnnSet an = singleGroupAnnSet [an]++-- | An 'AnnotationSet' with several 'Annotation's of the same kind.+singleGroupAnnSet :: forall tag. KnownAnnTag tag => [Annotation tag] -> AnnotationSet+singleGroupAnnSet ans = AnnotationSet {..}+ where+ asTypes = case eqT @tag @TypeTag of Just Refl -> ans; Nothing -> []+ asFields = case eqT @tag @FieldTag of Just Refl -> ans; Nothing -> []+ asVars = case eqT @tag @VarTag of Just Refl -> ans; Nothing -> []++-- | An 'AnnotationSet' built from all 3 kinds of 'Annotation'.+fullAnnSet :: [TypeAnn] -> [FieldAnn] -> [VarAnn] -> AnnotationSet+fullAnnSet asTypes asFields asVars = AnnotationSet {..}++-- | Returns 'True' if all 'Annotation's in the Set are unnecessary/empty/'noAnn'.+-- False otherwise.+isNoAnnSet :: AnnotationSet -> Bool+isNoAnnSet annSet = null asTypes && null asFields && null asVars+ where AnnotationSet {..} = minimizeAnnSet annSet++-- | Returns the amount of 'Annotation's that are necessary for a consistent+-- representation. See 'AnnotationSet'.+minAnnSetSize :: AnnotationSet -> Int+minAnnSetSize annSet = length asTypes + length asFields + length asVars+ where AnnotationSet {..} = minimizeAnnSet annSet++-- | Removes all unnecessary 'Annotation's. See 'AnnotationSet'.+minimizeAnnSet :: AnnotationSet -> AnnotationSet+minimizeAnnSet (AnnotationSet ts fs vs) = AnnotationSet {..}+ where+ asTypes = trimEndNoAnn ts+ asFields = trimEndNoAnn fs+ asVars = trimEndNoAnn vs++-- | Removes all unnecessary 'Annotation's from a list of the same type+trimEndNoAnn :: [Annotation tag] -> [Annotation tag]+trimEndNoAnn = foldr (\a lst -> if null lst && a == noAnn then [] else a : lst) []++--------------------------------------------------------------------------------+-- Rendering+--------------------------------------------------------------------------------++class Typeable (tag :: Kind.Type) => KnownAnnTag tag where annPrefix :: Text instance KnownAnnTag tag => Show (Annotation tag) where- show (Annotation x) = toString $ annPrefix @tag <> x+ show = printDocS True . renderDoc doesntNeedParens data TypeTag data FieldTag@@ -81,22 +177,34 @@ instance KnownAnnTag tag => RenderDoc (Annotation tag) where renderDoc _ = renderAnn -renderAnn :: forall tag. KnownAnnTag tag => Annotation tag -> Doc-renderAnn a@(Annotation text)- | a == noAnn = ""- | otherwise = textStrict $ annPrefix @tag <> text+instance KnownAnnTag tag => Buildable (Annotation tag) where+ build = buildRenderDoc --- | Prints empty prefix in case of @noAnn@.------ Such functionality is required in case when instruction--- has two annotations of the same type, former is empty--- and the latter is not. So that `PAIR noAnn noAnn noAnn %kek`--- is printed as `PAIR % %kek`-renderWEAnn :: forall tag. KnownAnnTag tag => Annotation tag -> Doc-renderWEAnn (Annotation text) = textStrict $ annPrefix @tag <> text+instance Show AnnotationSet where+ show = printDocS True . renderDoc doesntNeedParens -instance KnownAnnTag tag => Buildable (Annotation tag) where+instance RenderDoc AnnotationSet where+ renderDoc _ (AnnotationSet {..}) =+ renderAnnGroup asTypes <+> renderAnnGroup asFields <+> renderAnnGroup asVars++instance Buildable AnnotationSet where build = buildRenderDoc++-- | Renders a single 'Annotation', this is used in every rendering instance of it.+-- Note that this also renders empty ones/'noAnn's because a single 'Annotation'+-- does not have enough context to know if it can be omitted, use 'singleAnnSet'+-- if you want to hide it instead.+renderAnn :: forall tag. KnownAnnTag tag => Annotation tag -> Doc+renderAnn (Annotation text) = textStrict $ annPrefix @tag <> text++-- | Renders a list of 'Annotation's, omitting unnecessary empty ones/'noAnn'.+-- This is used (3 times) to render an 'AnnotationSet'.+renderAnnGroup :: KnownAnnTag tag => [Annotation tag] -> Doc+renderAnnGroup = hsep . map renderAnn . trimEndNoAnn++--------------------------------------------------------------------------------+-- Creation and conversions+-------------------------------------------------------------------------------- noAnn :: Annotation a noAnn = AnnotationUnsafe ""
src/Michelson/Untyped/Contract.hs view
@@ -23,6 +23,8 @@ , code :: [op] } deriving stock (Eq, Show, Functor, Data, Generic) +instance NFData op => NFData (Contract' op)+ instance (RenderDoc op) => RenderDoc (Contract' op) where renderDoc pn (Contract parameter storage code) = assertParensNotNeeded pn $ "parameter" <+> renderDoc needsParens (Prettier parameter) <> semi <$$>
src/Michelson/Untyped/EntryPoints.hs view
@@ -6,13 +6,17 @@ , epNameFromRefAnn , epNameToRefAnn , EpNameFromRefAnnError (..)+ , mkEntrypointsMap ) where import Data.Default (Default(..))-import Fmt (Buildable(..), (+|), (|+))+import qualified Data.Map as Map+import Fmt (Buildable(..), pretty, (+|), (|+)) import Test.QuickCheck (Arbitrary(..), suchThatMap) import Michelson.Untyped.Annotation+import Michelson.Untyped.Type+import Util.CLI -- | Entrypoint name. --@@ -20,8 +24,10 @@ -- Cannot be equal to "default", the reference implementation forbids that. -- Also, set of allowed characters should be the same as in annotations. newtype EpName = EpNameUnsafe { unEpName :: Text }- deriving (Show, Eq, Ord)+ deriving (Show, Eq, Ord, Generic) +instance NFData EpName+ pattern DefEpName :: EpName pattern DefEpName = EpNameUnsafe "" @@ -50,8 +56,10 @@ data EpNameFromRefAnnError = InEpNameBadAnnotation FieldAnn- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData EpNameFromRefAnnError+ instance Buildable EpNameFromRefAnnError where build = \case InEpNameBadAnnotation (Annotation an) ->@@ -72,3 +80,35 @@ instance Arbitrary FieldAnn => Arbitrary EpName where arbitrary = arbitrary `suchThatMap` (rightToMaybe . epNameFromRefAnn)++instance HasCLReader EpName where+ getReader = eitherReader parseEntrypointDo+ where+ parseEntrypointDo (toText -> txt) = do+ an <-+ mkAnnotation txt+ & first (mappend "Failed to parse entrypoint: " . pretty)+ epNameFromRefAnn an+ & first pretty+ getMetavar = "ENTRYPOINT"++-- | Given an untyped type, extract a map that maps entrypoint names to the+-- their parameter types. If there are duplicate entrypoints in the given Type+-- then the duplicate entrypoints at a deeper nesting level will get+-- overwritten with the ones that are on top.+mkEntrypointsMap :: Type -> Map EpName Type+mkEntrypointsMap (Type t _) = case t of+ -- We are only interested in `Or` branches to extract entrypoint+ -- annotations.+ TOr f1 f2 t1 t2 -> extractSubmap f1 t1 <> extractSubmap f2 t2+ _ -> mempty+ where+ extractSubmap :: FieldAnn -> Type -> Map EpName Type+ extractSubmap f1 t1 = let+ -- If the field annotation is not empty then merge this field+ -- annotation/type pair with the entrypoint map of inner type. Else+ -- just fetch entrypoint map of inner type.+ innerMap = mkEntrypointsMap t1+ in case epNameFromParamAnn f1 of+ Just epName -> Map.singleton epName t1 <> innerMap+ Nothing -> innerMap
src/Michelson/Untyped/Ext.hs view
@@ -38,6 +38,8 @@ | UPRINT PrintComment -- ^ Print a comment with optional embedded @StackRef@s deriving stock (Eq, Show, Data, Generic, Functor) +instance NFData op => NFData (ExtInstrAbstract op)+ instance RenderDoc op => RenderDoc (ExtInstrAbstract op) where renderDoc _ = \case@@ -55,11 +57,15 @@ newtype StackRef = StackRef Natural deriving stock (Eq, Show, Data, Generic) +instance NFData StackRef+ instance Buildable StackRef where build (StackRef i) = "%[" <> show i <> "]" newtype Var = Var T.Text deriving stock (Eq, Show, Ord, Data, Generic) +instance NFData Var+ instance Buildable Var where build = genericF @@ -69,6 +75,8 @@ | TyCon Type deriving stock (Eq, Show, Data, Generic) +instance NFData TyVar+ instance Buildable TyVar where build = genericF @@ -79,6 +87,8 @@ | StkCons TyVar StackTypePattern deriving stock (Eq, Show, Data, Generic) +instance NFData StackTypePattern+ -- | Convert 'StackTypePattern' to a list of types. Also returns -- 'Bool' which is 'True' if the pattern is a fixed list of types and -- 'False' if it's a pattern match on the head of the stack.@@ -103,6 +113,8 @@ , outPattern :: StackTypePattern } deriving stock (Eq, Show, Data, Generic) +instance NFData StackFn+ instance Buildable StackFn where build = genericF @@ -118,6 +130,8 @@ { unUPrintComment :: [Either T.Text StackRef] } deriving stock (Eq, Show, Data, Generic) +instance NFData PrintComment+ instance Buildable PrintComment where build = foldMap (either build build) . unUPrintComment @@ -127,6 +141,8 @@ , tassComment :: PrintComment , tassInstrs :: [op] } deriving stock (Eq, Show, Functor, Data, Generic)++instance NFData op => NFData (TestAssert op) instance Buildable code => Buildable (TestAssert code) where build = genericF
src/Michelson/Untyped/Instr.hs view
@@ -20,11 +20,14 @@ import Generics.SYB (everywhere, mkT) import Text.PrettyPrint.Leijen.Text (Doc, align, braces, enclose, indent, line, nest, space, text, (<$$>), (<+>))+import qualified Text.Show import Michelson.ErrorPos (InstrCallStack) import Michelson.Printer.Util- (RenderDoc(..), buildRenderDoc, doesntNeedParens, needsParens, renderOpsList, spaces)-import Michelson.Untyped.Annotation (Annotation, FieldAnn, TypeAnn, VarAnn, noAnn, renderWEAnn)+ (RenderDoc(..), buildRenderDoc, doesntNeedParens, needsParens, printDocS,+ renderOpsList, spaces)+import Michelson.Untyped.Annotation+ (Annotation, FieldAnn, KnownAnnTag, TypeAnn, VarAnn, fullAnnSet, singleAnnSet) import Michelson.Untyped.Contract (Contract'(..)) import Michelson.Untyped.Ext (ExtInstrAbstract) import Michelson.Untyped.Type (Comparable, Type)@@ -45,6 +48,8 @@ | WithSrcEx InstrCallStack ExpandedOp deriving stock (Show, Eq, Data, Generic) +instance NFData ExpandedOp+ instance RenderDoc ExpandedOp where renderDoc pn (WithSrcEx _ op) = renderDoc pn op renderDoc pn (PrimEx i) = renderDoc pn i@@ -188,8 +193,13 @@ | SENDER VarAnn | ADDRESS VarAnn | CHAIN_ID VarAnn- deriving stock (Eq, Show, Functor, Data, Generic)+ deriving stock (Eq, Functor, Data, Generic) +instance RenderDoc (InstrAbstract op) => Show (InstrAbstract op) where+ show = printDocS True . renderDoc doesntNeedParens++instance NFData op => NFData (InstrAbstract op)+ instance (RenderDoc op) => RenderDoc (InstrAbstract op) where renderDoc pn = \case EXT extInstr -> renderDoc pn extInstr@@ -200,23 +210,23 @@ DIG n -> "DIG" <+> text (show n) DUG n -> "DUG" <+> text (show n) PUSH va t v -> "PUSH" <+> renderAnnot va <+> renderTy t <+> renderDoc needsParens v- SOME ta va -> "SOME" <+> renderAnnot ta <+> renderAnnot va- NONE ta va t -> "NONE" <+> renderAnnot ta <+> renderAnnot va <+> renderTy t- UNIT ta va -> "UNIT" <+> renderAnnot ta <+> renderAnnot va+ SOME ta va -> "SOME" <+> renderAnnots [ta] [] [va]+ NONE ta va t -> "NONE" <+> renderAnnots [ta] [] [va] <+> renderTy t+ UNIT ta va -> "UNIT" <+> renderAnnots [ta] [] [va] IF_NONE x y -> "IF_NONE" <+> nest 9 (renderOps x) <$$> spaces 8 <> nest 9 (renderOps y)- PAIR ta va fa1 fa2 -> "PAIR" <+> renderAnnot ta <+> renderAnnot va <+> renderTwoAnnotations fa1 fa2- CAR va fa -> "CAR" <+> renderAnnot va <+> renderAnnot fa- CDR va fa -> "CDR" <+> renderAnnot va <+> renderAnnot fa- LEFT ta va fa1 fa2 t -> "LEFT" <+> renderAnnot ta <+> renderAnnot va <+> renderTwoAnnotations fa1 fa2 <+> renderTy t- RIGHT ta va fa1 fa2 t -> "RIGHT" <+> renderAnnot ta <+> renderAnnot va <+> renderTwoAnnotations fa1 fa2 <+> renderTy t+ PAIR ta va fa1 fa2 -> "PAIR" <+> renderAnnots [ta] [fa1, fa2] [va]+ CAR va fa -> "CAR" <+> renderAnnots [] [fa] [va]+ CDR va fa -> "CDR" <+> renderAnnots [] [fa] [va]+ LEFT ta va fa1 fa2 t -> "LEFT" <+> renderAnnots [ta] [fa1, fa2] [va] <+> renderTy t+ RIGHT ta va fa1 fa2 t -> "RIGHT" <+> renderAnnots [ta] [fa1, fa2] [va] <+> renderTy t IF_LEFT x y -> "IF_LEFT" <+> nest 9 (renderOps x) <$$> spaces 8 <> nest 9 (renderOps y)- NIL ta va t -> "NIL" <+> renderAnnot ta <+> renderAnnot va <+> renderTy t+ NIL ta va t -> "NIL" <+> renderAnnots [ta] [] [va] <+> renderTy t CONS va -> "CONS" <+> renderAnnot va IF_CONS x y -> "IF_CONS" <+> nest 9 (renderOps x) <$$> spaces 8 <> nest 9 (renderOps y) SIZE va -> "SIZE" <+> renderAnnot va- EMPTY_SET ta va t -> "EMPTY_SET" <+> renderAnnot ta <+> renderAnnot va <+> renderComp t- EMPTY_MAP ta va c t -> "EMPTY_MAP" <+> renderAnnot ta <+> renderAnnot va <+> renderComp c <+> renderTy t- EMPTY_BIG_MAP ta va c t -> "EMPTY_BIG_MAP" <+> renderAnnot ta <+> renderAnnot va <+> renderComp c <+> renderTy t+ EMPTY_SET ta va t -> "EMPTY_SET" <+> renderAnnots [ta] [] [va] <+> renderComp t+ EMPTY_MAP ta va c t -> "EMPTY_MAP" <+> renderAnnots [ta] [] [va] <+> renderComp c <+> renderTy t+ EMPTY_BIG_MAP ta va c t -> "EMPTY_BIG_MAP" <+> renderAnnots [ta] [] [va] <+> renderComp c <+> renderTy t MAP va s -> "MAP" <+> renderAnnot va <$$> spaces 4 <> nest 5 (renderOps s) ITER s -> "ITER" <+> nest 6 (renderOps s) MEM va -> "MEM" <+> renderAnnot va@@ -225,16 +235,16 @@ IF x y -> "IF" <+> nest 4 (renderOps x) <$$> spaces 3 <> nest 4 (renderOps y) LOOP s -> "LOOP" <+> nest 6 (renderOps s) LOOP_LEFT s -> "LOOP_LEFT" <+> nest 11 (renderOps s)- LAMBDA va t r s -> "LAMBDA" <+> renderAnnot va <+> renderTy t <+> renderTy r <$$> spaces 7 <> nest 8 (renderOps s)+ LAMBDA va t r s -> "LAMBDA" <+> renderAnnot va <$$> (spaces 2 <> renderTy t) <$$> (spaces 2 <> renderTy r) <$$> spaces 2 <> nest 3 (renderOps s) EXEC va -> "EXEC" <+> renderAnnot va APPLY va -> "APPLY" <+> renderAnnot va DIP s -> "DIP" <+> nest 5 (renderOps s)- DIPN n s -> "DIP" <+> text (show n) <> line <> indent (3 + (length $ show @Text n)) (renderOps s)+ DIPN n s -> "DIP" <+> text (show n) <> line <> indent 4 (renderOps s) FAILWITH -> "FAILWITH" CAST va t -> "CAST" <+> renderAnnot va <+> renderTy t RENAME va -> "RENAME" <+> renderAnnot va PACK va -> "PACK" <+> renderAnnot va- UNPACK ta va t -> "UNPACK" <+> renderAnnot ta <+> renderAnnot va <+> renderTy t+ UNPACK ta va t -> "UNPACK" <+> renderAnnots [ta] [] [va] <+> renderTy t CONCAT va -> "CONCAT" <+> renderAnnot va SLICE va -> "SLICE" <+> renderAnnot va ISNAT va -> "ISNAT" <+> renderAnnot va@@ -258,13 +268,13 @@ LE va -> "LE" <+> renderAnnot va GE va -> "GE" <+> renderAnnot va INT va -> "INT" <+> renderAnnot va- SELF va fa -> "SELF" <+> renderAnnot va <+> renderAnnot fa- CONTRACT va fa t -> "CONTRACT" <+> renderAnnot va <+> renderAnnot fa <+> renderTy t+ SELF va fa -> "SELF" <+> renderAnnots [] [fa] [va]+ CONTRACT va fa t -> "CONTRACT" <+> renderAnnots [] [fa] [va] <+> renderTy t TRANSFER_TOKENS va -> "TRANSFER_TOKENS" <+> renderAnnot va SET_DELEGATE va -> "SET_DELEGATE" <+> renderAnnot va CREATE_CONTRACT va1 va2 contract -> let body = enclose space space $ align $ (renderDoc doesntNeedParens contract)- in "CREATE_CONTRACT" <+> renderTwoAnnotations va1 va2 <$$> (indent 2 $ braces $ body)+ in "CREATE_CONTRACT" <+> renderAnnots [] [] [va1, va2] <$$> (indent 2 $ braces $ body) IMPLICIT_ACCOUNT va -> "IMPLICIT_ACCOUNT" <+> renderAnnot va NOW va -> "NOW" <+> renderAnnot va AMOUNT va -> "AMOUNT" <+> renderAnnot va@@ -283,14 +293,12 @@ renderTy = renderDoc @Type needsParens renderComp = renderDoc @Comparable needsParens renderOps = renderOpsList False- renderTwoAnnotations ann1 ann2 =- -- If both annotations are noAnn we don't want to explicitly print them- if (ann1 /= noAnn || ann2 /= noAnn)- then renderWEAnn ann1 <+> renderWEAnn ann2- else renderDoc doesntNeedParens ann1 <+> renderDoc doesntNeedParens ann2 - renderAnnot :: RenderDoc (Annotation tag) => Annotation tag -> Doc- renderAnnot = renderDoc @(Annotation _) needsParens+ renderAnnot :: KnownAnnTag tag => Annotation tag -> Doc+ renderAnnot = renderDoc doesntNeedParens . singleAnnSet++ renderAnnots :: [TypeAnn] -> [FieldAnn] -> [VarAnn] -> Doc+ renderAnnots ts fs vs = renderDoc doesntNeedParens $ fullAnnSet ts fs vs isRenderable = \case EXT extInstr -> isRenderable extInstr
src/Michelson/Untyped/Type.hs view
@@ -60,27 +60,30 @@ import Michelson.Printer.Util (Prettier(..), RenderContext, RenderDoc(..), addParens, buildRenderDoc, doesntNeedParens, needsParens, wrapInParens)-import Michelson.Untyped.Annotation (FieldAnn, TypeAnn, noAnn, renderAnn)+import Michelson.Untyped.Annotation+ (AnnotationSet, FieldAnn, TypeAnn, emptyAnnSet, fullAnnSet, noAnn, singleAnnSet) -- Annotated type data Type = Type ~T TypeAnn deriving stock (Eq, Show, Data, Generic) +instance NFData Type+ instance RenderDoc Comparable where renderDoc np (Comparable ct ta) = addParens np $- renderCT ct <+> renderAnn ta+ renderCT ct <+> renderDoc doesntNeedParens (singleAnnSet ta) instance RenderDoc (Prettier Type) where renderDoc pn (Prettier w) = case w of- (Type t ta) -> renderType t False pn (Just ta) Nothing+ (Type t ta) -> renderType t False pn (singleAnnSet ta) instance RenderDoc Type where- renderDoc pn (Type t ta) = renderType t True pn (Just ta) Nothing+ renderDoc pn (Type t ta) = renderType t True pn (singleAnnSet ta) instance RenderDoc T where- renderDoc pn t = renderType t True pn Nothing Nothing+ renderDoc pn t = renderType t True pn emptyAnnSet -- Ordering between different kinds of annotations is not significant, -- but ordering among annotations of the same kind is. Annotations@@ -93,78 +96,74 @@ :: T -> Bool -> RenderContext- -> Maybe TypeAnn- -> Maybe FieldAnn+ -> AnnotationSet -> Doc-renderType t forceSingleLine pn mta mfa =- let rta = case mta of Just ta -> renderDoc doesntNeedParens ta; Nothing -> ""- rfa = case mfa of Just fa -> renderDoc doesntNeedParens fa; Nothing -> ""- recRenderer (Type tt ta) mfa' =- renderType tt forceSingleLine needsParens (Just ta) mfa'+renderType t forceSingleLine pn annSet =+ let annDoc = renderDoc doesntNeedParens annSet+ recRenderer t' annSet' = renderType t' forceSingleLine needsParens annSet' renderBranches d1 d2 = if forceSingleLine then (d1 <+> d2) else align $ softbreak <> (d1 <$> d2) in case t of- Tc ct -> wrapInParens pn $ renderCT ct :| [rta, rfa]- TKey -> wrapInParens pn $ "key" :| [rta, rfa]- TUnit -> wrapInParens pn $ "unit" :| [rta, rfa]- TSignature -> wrapInParens pn $ "signature" :| [rta, rfa]- TChainId -> wrapInParens pn $ "chain_id" :| [rta, rfa]- TOperation -> wrapInParens pn $ "operation" :| [rta, rfa]+ Tc ct -> wrapInParens pn $ renderCT ct :| [annDoc]+ TKey -> wrapInParens pn $ "key" :| [annDoc]+ TUnit -> wrapInParens pn $ "unit" :| [annDoc]+ TSignature -> wrapInParens pn $ "signature" :| [annDoc]+ TChainId -> wrapInParens pn $ "chain_id" :| [annDoc]+ TOperation -> wrapInParens pn $ "operation" :| [annDoc] TOption (Type t1 ta1) -> addParens pn $- "option" <+> rta <+> rfa- <+> renderType t1 forceSingleLine needsParens (Just ta1) Nothing+ "option" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1) TList (Type t1 ta1) -> addParens pn $- "list" <+> rta <+> rfa- <+> renderType t1 forceSingleLine needsParens (Just ta1) Nothing+ "list" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1) TSet (Comparable ct1 ta1) -> addParens pn $- "set" <+> rta <+> rfa- <+> renderType (Tc ct1) forceSingleLine needsParens (Just ta1) Nothing+ "set" <+> annDoc <+> recRenderer (Tc ct1) (singleAnnSet ta1) TContract (Type t1 ta1) -> addParens pn $- "contract" <+> rta <+> rfa- <+> renderType t1 forceSingleLine needsParens (Just ta1) Nothing+ "contract" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1) - TPair fa1 fa2 t1 t2 ->+ TPair fa1 fa2 (Type t1 ta1) (Type t2 ta2) -> addParens pn $- "pair" <+> rta <+> rfa <+>- (renderBranches- (recRenderer t1 (Just fa1)) (recRenderer t2 (Just fa2)))+ "pair" <+> annDoc <+>+ renderBranches+ (recRenderer t1 $ fullAnnSet [ta1] [fa1] [])+ (recRenderer t2 $ fullAnnSet [ta2] [fa2] []) - TOr fa1 fa2 t1 t2 ->+ TOr fa1 fa2 (Type t1 ta1) (Type t2 ta2) -> addParens pn $- "or" <+> rta <+> rfa <+>- (renderBranches- (recRenderer t1 (Just fa1)) (recRenderer t2 (Just fa2)))+ "or" <+> annDoc <+>+ renderBranches+ (recRenderer t1 $ fullAnnSet [ta1] [fa1] [])+ (recRenderer t2 $ fullAnnSet [ta2] [fa2] []) - TLambda t1 t2 ->+ TLambda (Type t1 ta1) (Type t2 ta2) -> addParens pn $- "lambda" <+> rta <+> rfa <+>- (renderBranches- (recRenderer t1 Nothing) (recRenderer t2 Nothing))+ "lambda" <+> annDoc <+>+ renderBranches+ (recRenderer t1 $ singleAnnSet ta1)+ (recRenderer t2 $ singleAnnSet ta2) - TMap (Comparable ct1 ta1) t2 ->+ TMap (Comparable ct1 ta1) (Type t2 ta2) -> addParens pn $- "map" <+> rta <+> rfa <+>- (renderBranches- (renderType (Tc ct1) forceSingleLine needsParens (Just ta1) Nothing)- (recRenderer t2 Nothing))+ "map" <+> annDoc <+>+ renderBranches+ (recRenderer (Tc ct1) $ singleAnnSet ta1)+ (recRenderer t2 $ singleAnnSet ta2) - TBigMap (Comparable ct1 ta1) t2 ->+ TBigMap (Comparable ct1 ta1) (Type t2 ta2) -> addParens pn $- "big_map" <+> rta <+> rfa <+>- (renderBranches- (renderType (Tc ct1) forceSingleLine needsParens (Just ta1) Nothing)- (recRenderer t2 Nothing))+ "big_map" <+> annDoc <+>+ renderBranches+ (recRenderer (Tc ct1) $ singleAnnSet ta1)+ (recRenderer t2 $ singleAnnSet ta2) renderCT :: CT -> Doc renderCT = \case@@ -191,6 +190,8 @@ instance Buildable Comparable where build = buildRenderDoc +instance NFData Comparable+ compToType :: Comparable -> Type compToType (Comparable ct tn) = Type (Tc ct) tn @@ -220,6 +221,8 @@ instance Buildable T where build = buildRenderDoc +instance NFData T+ -- Comparable Sub-Type data CT = CInt@@ -235,6 +238,8 @@ instance Buildable CT where build = buildRenderDoc++instance NFData CT pattern Tint :: T pattern Tint = Tc CInt
src/Michelson/Untyped/Value.hs view
@@ -40,13 +40,19 @@ | ValueLambda (NonEmpty op) deriving stock (Eq, Show, Functor, Data, Generic) +instance NFData op => NFData (Value' op)+ data Elt op = Elt (Value' op) (Value' op) deriving stock (Eq, Show, Functor, Data, Generic) +instance NFData op => NFData (Elt op)+ -- | ByteString does not have an instance for ToJSON and FromJSON, to -- avoid orphan type class instances, make a new type wrapper around it. newtype InternalByteString = InternalByteString ByteString- deriving stock (Data, Eq, Show)+ deriving stock (Data, Eq, Show, Generic)++instance NFData InternalByteString unInternalByteString :: InternalByteString -> ByteString unInternalByteString (InternalByteString bs) = bs
+ src/Morley/CLI.hs view
@@ -0,0 +1,145 @@+{-# OPTIONS_GHC -Wno-orphans #-}++-- | Utilities for parsing Morley types using @optparse-applicative@.++module Morley.CLI+ ( -- * Options+ contractFileOption+ , nowOption+ , maxStepsOption+ , dbPathOption+ , txDataOption+ , keyHashOption+ , valueOption+ , mutezOption+ , addressOption+ , onelineOption+ , entrypointOption+ , mTextOption+ ) where++import Options.Applicative (help, long, metavar, option, strOption, switch)+import qualified Options.Applicative as Opt++import qualified Michelson.Parser as P+import Michelson.Runtime (TxData(..))+import Michelson.Runtime.GState (genesisAddress)+import Michelson.Text (MText)+import Michelson.Untyped (EpName)+import qualified Michelson.Untyped as U+import Tezos.Address (Address)+import Tezos.Core (Mutez, Timestamp, parseTimestamp, timestampFromSeconds)+import Tezos.Crypto+import Util.CLI+import Util.Named++----------------------------------------------------------------------------+-- These options are mostly specifications of 'mkCLOptionParser'+-- for concrete types (with more specific names).+----------------------------------------------------------------------------++-- | Parser for path to a contract code.+contractFileOption :: Opt.Parser FilePath+contractFileOption = strOption $+ long "contract" <>+ metavar "FILEPATH" <>+ help "Path to contract file"++-- | Parser for the time returned by @NOW@ instruction.+nowOption :: Opt.Parser (Maybe Timestamp)+nowOption = optional $ option parser $+ long "now" <>+ metavar "TIMESTAMP" <>+ help "Timestamp that you want the runtime interpreter to use (default is now)"+ where+ parser =+ (timestampFromSeconds <$> Opt.auto) <|>+ Opt.maybeReader (parseTimestamp . toText)++-- | Parser for gas limit on contract execution.+maxStepsOption :: Opt.Parser Word64+maxStepsOption = mkCLOptionParser+ (Just 100500)+ (#name .! "max-steps")+ (#help .! "Max steps that you want the runtime interpreter to use")++-- | Parser for path to database with Morley state.+dbPathOption :: Opt.Parser FilePath+dbPathOption = Opt.strOption $+ long "db" <>+ metavar "FILEPATH" <>+ Opt.value "db.json" <>+ help "Path to DB with data which is used instead of real blockchain data" <>+ Opt.showDefault++-- | Parser for transaction parameters.+txDataOption :: Opt.Parser TxData+txDataOption =+ mkTxData+ <$> addressOption (Just genesisAddress)+ (#name .! "sender") (#help .! "Sender address")+ <*> valueOption Nothing+ (#name .! "parameter") (#help .! "Parameter of passed contract")+ <*> mutezOption (Just minBound)+ (#name .! "amount") (#help .! "Amount sent by a transaction")+ <*> entrypointOption (#name .! "entrypoint") (#help .! "Entrypoint to call")+ where+ mkTxData :: Address -> U.Value -> Mutez -> EpName -> TxData+ mkTxData addr param amount epName =+ TxData+ { tdSenderAddress = addr+ , tdParameter = param+ , tdEntrypoint = epName+ , tdAmount = amount+ }++-- | Generic parser to read an option of 'KeyHash' type.+keyHashOption ::+ Maybe KeyHash -> "name" :! String -> "help" :! String -> Opt.Parser KeyHash+keyHashOption = mkCLOptionParser++-- | Generic parser to read an option of 'U.Value' type.+valueOption ::+ Maybe U.Value -> "name" :! String -> "help" :! String -> Opt.Parser U.Value+valueOption = mkCLOptionParser++-- | Generic parser to read an option of 'Mutez' type.+mutezOption ::+ Maybe Mutez -> "name" :! String -> "help" :! String -> Opt.Parser Mutez+mutezOption = mkCLOptionParser++-- | Generic parser to read an option of 'Address' type.+addressOption ::+ Maybe Address -> "name" :! String -> "help" :! String -> Opt.Parser Address+addressOption = mkCLOptionParser++-- | @--oneline@ flag.+onelineOption :: Opt.Parser Bool+onelineOption = switch (+ long "oneline" <>+ help "Force single line output")++-- | Generic parser to read an option of 'EpName' type.+entrypointOption :: "name" :! String -> "help" :! String -> Opt.Parser EpName+entrypointOption = mkCLOptionParser (Just U.DefEpName)++-- | Generic parser to read an option of 'MText' type.+mTextOption ::+ Maybe MText -> "name" :! String -> "help" :! String -> Opt.Parser MText+mTextOption = mkCLOptionParser++----------------------------------------------------------------------------+-- 'HasCLReader' orphan instances (better to avoid)+----------------------------------------------------------------------------++-- This instance uses parser which is not in the place where 'U.Value'+-- is defined, hence it is orphan.+instance HasCLReader U.Value where+ getReader = eitherReader parseValue+ where+ parseValue :: String -> Either String U.Value+ parseValue =+ first (mappend "Failed to parse value: " . displayException) .+ P.parseExpandValue .+ toText+ getMetavar = "MICHELSON VALUE"
+ src/Morley/Micheline.hs view
@@ -0,0 +1,64 @@+-- | Module that provides type classes for converting to and from low-level+-- Micheline representation.+module Morley.Micheline+ ( ToExpression (..)+ , FromExpression (..)+ ) where++import Data.Sequence (fromList)+import Data.Singletons (pattern FromSing, Sing, SingI, withSingI)+import Tezos.Common.Binary (decode, encode)+import Tezos.V005.Micheline+ (Expression(..), MichelinePrimAp(..), MichelinePrimitive(..))++import Michelson.Interpret.Pack (encodeValue', packCode', packNotedT', packT')+import Michelson.Interpret.Unpack (unpackInstr', unpackValue')+import Michelson.Typed+ (FullContract(..), HasNoOp, Instr(..), Notes(..), T(..), Value, unParamNotes)+import Michelson.Typed.Scope (UnpackedValScope)+import Michelson.Untyped.Instr (ExpandedOp)++-- | 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 = decode . packCode'++instance ToExpression T where+ toExpression (FromSing (ts :: Sing t)) =+ decode $ withSingI ts $ (packT' @t)++instance SingI t => ToExpression (Notes t) where+ toExpression = decode . packNotedT'++instance (SingI t, HasNoOp t) => ToExpression (Value t) where+ toExpression = decode . encodeValue'++instance ToExpression (FullContract cp st) where+ toExpression FullContract{..} = Expression_Seq $ fromList+ [ Expression_Prim $+ MichelinePrimAp (MichelinePrimitive "parameter")+ (fromList [toExpression $ unParamNotes $ fcParamNotesSafe])+ (fromList [])+ , Expression_Prim $+ MichelinePrimAp (MichelinePrimitive "storage")+ (fromList [toExpression $ fcStoreNotes])+ (fromList [])+ , Expression_Prim $+ MichelinePrimAp (MichelinePrimitive "code")+ (fromList [toExpression fcCode])+ (fromList [])+ ]++-- | Type class that provides the ability to convert+-- something from a Micheline Expression.+class FromExpression a where+ fromExpression :: Expression -> Maybe a++instance UnpackedValScope t => FromExpression (Value t) where+ fromExpression = rightToMaybe . unpackValue' . ("\05" <>) . encode++instance UnpackedValScope t => FromExpression [ExpandedOp] where+ fromExpression = rightToMaybe . unpackInstr' . encode
src/Tezos/Address.hs view
@@ -29,20 +29,25 @@ import Michelson.Text import Tezos.Crypto+import Util.CLI -- TODO: we should probably have a `Hash` type. -- | Hash of origination command for some contract. newtype ContractHash = ContractHash ByteString- deriving stock (Show, Eq, Ord)+ deriving stock (Show, Eq, Ord, Generic) +instance NFData ContractHash+ -- | Data type corresponding to address structure in Tezos. data Address = KeyAddress KeyHash -- ^ `tz` address which is a hash of a public key. | ContractAddress ContractHash -- ^ `KT` address which corresponds to a callable contract.- deriving stock (Show, Eq, Ord)+ deriving stock (Show, Eq, Ord, Generic) +instance NFData Address+ -- | Smart constructor for 'KeyAddress'. mkKeyAddress :: PublicKey -> Address mkKeyAddress = KeyAddress . hashKey@@ -97,8 +102,10 @@ -- ^ Address is not in Base58Check format. | ParseAddressBothFailed CryptoParseError ParseContractAddressError -- ^ Both address parsers failed with some error.- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData ParseAddressError+ instance Buildable.Buildable ParseAddressError where build = \case@@ -132,8 +139,10 @@ = ParseContractAddressWrongBase58Check | ParseContractAddressWrongTag ByteString | ParseContractAddressWrongSize Int- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData ParseContractAddressError+ instance Buildable.Buildable ParseContractAddressError where build = \case@@ -166,6 +175,14 @@ -- It was deduced empirically. contractAddressPrefix :: ByteString contractAddressPrefix = "\2\90\121"++instance HasCLReader Address where+ getReader = eitherReader parseAddrDo+ where+ parseAddrDo addr =+ either (Left . mappend "Failed to parse address: " . pretty) Right $+ parseAddress $ toText addr+ getMetavar = "ADDRESS" ---------------------------------------------------------------------------- -- Aeson instances
src/Tezos/Core.hs view
@@ -54,10 +54,12 @@ import Formatting.Buildable (Buildable(build)) import qualified Language.Haskell.TH.Quote as TH import Language.Haskell.TH.Syntax (liftData)+import qualified Options.Applicative as Opt import Test.QuickCheck (Arbitrary(..), vector) import Michelson.Text import Tezos.Crypto+import Util.CLI ---------------------------------------------------------------------------- -- Mutez@@ -75,6 +77,12 @@ -- This value was checked against the reference implementation. maxBound = Mutez 9223372036854775807 +instance HasCLReader Mutez where+ getReader = maybe (readerError "Invalid mutez") pure . mkMutez =<< Opt.auto+ getMetavar = "MUTEZ"++instance NFData Mutez+ -- | Safely create 'Mutez' checking for overflow. mkMutez :: Word64 -> Maybe Mutez mkMutez n@@ -159,6 +167,8 @@ { unTimestamp :: POSIXTime } deriving stock (Show, Eq, Ord, Data, Generic) +instance NFData Timestamp+ timestampToSeconds :: Integral a => Timestamp -> a timestampToSeconds = round . unTimestamp {-# INLINE timestampToSeconds #-}@@ -250,7 +260,9 @@ -- signed data "in order to add extra replay protection between the main -- chain and the test chain". newtype ChainId = ChainIdUnsafe { unChainId :: ByteString }- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic)++instance NFData ChainId -- | Construct chain ID from raw bytes. mkChainId :: ByteString -> Maybe ChainId
src/Tezos/Crypto.hs view
@@ -87,6 +87,7 @@ import qualified Tezos.Crypto.P256 as P256 import qualified Tezos.Crypto.Secp256k1 as Secp256k1 import Tezos.Crypto.Util+import Util.CLI ---------------------------------------------------------------------------- -- Types, instances, conversions@@ -101,8 +102,10 @@ -- ^ Public key that uses the secp256k1 cryptographic curve. | PublicKeyP256 P256.PublicKey -- ^ Public key that uses the NIST P-256 cryptographic curve.- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData PublicKey+ instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary @@ -115,8 +118,10 @@ -- ^ Secret key that uses the secp256k1 cryptographic curve. | SecretKeyP256 P256.SecretKey -- ^ Secret key that uses the NIST P-256 cryptographic curve.- deriving stock (Show, Eq)+ deriving stock (Show, Eq, Generic) +instance NFData SecretKey+ -- | Deterministicaly generate a secret key from seed. -- Type of the key depends on seed length. detSecretKey :: HasCallStack => ByteString -> SecretKey@@ -160,8 +165,10 @@ -- ^ Signature that uses the NIST P-256 cryptographic curve. | SignatureGeneric ByteString -- ^ Generic signature for which curve is unknown.- deriving stock (Show)+ deriving stock (Show, Generic) +instance NFData Signature+ -- This instance slightly differs from the default one. If one -- signature is generic and the other one is not, they still may be -- equal if they have the same byte representation.@@ -341,19 +348,23 @@ = KeyHashEd25519 | KeyHashSecp256k1 | KeyHashP256- deriving stock (Show, Eq, Ord, Bounded, Enum)+ deriving stock (Show, Eq, Ord, Bounded, Enum, Generic) instance Arbitrary KeyHashTag where arbitrary = elements [minBound .. ] +instance NFData KeyHashTag+ -- | Blake2b_160 hash of a public key. data KeyHash = KeyHash { khTag :: KeyHashTag -- ^ We store which curve was used because it affects formatting. , khBytes :: ByteString -- ^ Hash itself.- } deriving stock (Show, Eq, Ord)+ } deriving stock (Show, Eq, Ord, Generic) +instance NFData KeyHash+ -- | Length of key hash in bytes (only hash itself, no tags, checksums -- or anything). keyHashLengthBytes :: Integral n => n@@ -396,3 +407,7 @@ KeyHashEd25519 -> "\006\161\159" KeyHashSecp256k1 -> "\006\161\161" KeyHashP256 -> "\006\161\164"++instance HasCLReader KeyHash where+ getReader = eitherReader (first pretty . parseKeyHash . toText)+ getMetavar = "KEY_HASH"
src/Tezos/Crypto/Ed25519.hs view
@@ -49,16 +49,20 @@ -- | ED25519 public cryptographic key. newtype PublicKey = PublicKey { unPublicKey :: Ed25519.PublicKey- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary +instance NFData PublicKey+ -- | ED25519 secret cryptographic key. newtype SecretKey = SecretKey { unSecretKey :: Ed25519.SecretKey- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData SecretKey+ -- | Deterministicaly generate a secret key from seed. detSecretKey :: ByteString -> SecretKey detSecretKey seed = SecretKey $ deterministic seed Ed25519.generateSecretKey@@ -73,10 +77,12 @@ -- | ED25519 cryptographic signature. newtype Signature = Signature { unSignature :: Ed25519.Signature- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) instance Arbitrary Signature where arbitrary = sign <$> arbitrary <*> (encodeUtf8 @String <$> arbitrary)++instance NFData Signature ---------------------------------------------------------------------------- -- Conversion to/from raw bytes (no checksums, tags or anything)
src/Tezos/Crypto/P256.hs view
@@ -48,16 +48,20 @@ -- | P256 public cryptographic key. newtype PublicKey = PublicKey { unPublicKey :: ByteString- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary +instance NFData PublicKey+ -- | P256 secret cryptographic key. newtype SecretKey = SecretKey { unSecretKey :: ByteString- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData SecretKey+ -- | Deterministicaly generate a secret key from seed. detSecretKey :: ByteString -> SecretKey detSecretKey seed =@@ -73,10 +77,12 @@ -- | P256 cryptographic signature. newtype Signature = Signature { unSignature :: ByteString- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) instance Arbitrary Signature where arbitrary = Signature . BS.pack <$> replicateM signatureLengthBytes arbitrary++instance NFData Signature ---------------------------------------------------------------------------- -- Conversion to/from raw bytes (no checksums, tags or anything)
src/Tezos/Crypto/Secp256k1.hs view
@@ -33,7 +33,8 @@ import Crypto.Number.Serialize (i2ospOf_, os2ip) import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.ECC.Generate as ECC.Generate-import Crypto.PubKey.ECC.Types (Curve, CurveName(..), Point(..), curveSizeBits, getCurveByName)+import Crypto.PubKey.ECC.Types (Curve(..), CurveName(..), CurveCommon(..), CurvePrime(..)+ ,Point(..), curveSizeBits, getCurveByName) import Crypto.Random (MonadRandom, drgNewSeed, seedFromInteger, withDRG) import Data.ByteArray (ByteArray, ByteArrayAccess) import qualified Data.ByteArray as BA@@ -64,7 +65,7 @@ -- public key. -- -- TODO (#18) remove it.- } deriving stock (Show)+ } deriving stock (Show, Generic) -- TODO (#18): derive it instead once the above hack is removed. instance Eq PublicKey where@@ -73,11 +74,26 @@ instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary +rnfCurve :: Curve -> ()+rnfCurve cu =+ case cu of+ CurveF2m c -> rnf c+ CurveFP (CurvePrime i (CurveCommon a b c d e)) ->+ rnf (i, a, b, c, d, e)++instance NFData PublicKey where+ rnf (PublicKey (ECDSA.PublicKey cu q) bytes)+ = rnfCurve cu `seq` rnf (bytes, q)+ -- | Secp256k1 secret cryptographic key. newtype SecretKey = SecretKey { unSecretKey :: ECDSA.KeyPair- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) +instance NFData SecretKey where+ rnf (SecretKey (ECDSA.KeyPair cu pp pn)) =+ rnfCurve cu `seq` rnf (pp, pn)+ -- | Deterministicaly generate a secret key from seed. detSecretKey :: ByteString -> SecretKey detSecretKey seed = deterministic seed $ detSecretKeyDo@@ -100,7 +116,7 @@ -- | Secp256k1 cryptographic signature. newtype Signature = Signature { unSignature :: ECDSA.Signature- } deriving stock (Show, Eq)+ } deriving stock (Show, Eq, Generic) instance Arbitrary Signature where arbitrary = do@@ -109,6 +125,9 @@ return $ fst $ withDRG seed $ do sk <- detSecretKeyDo sign sk (one byteToSign)++instance NFData Signature where+ rnf (Signature (ECDSA.Signature a b)) = rnf a `seq` rnf b ---------------------------------------------------------------------------- -- Conversion to/from raw bytes (no checksums, tags or anything)
src/Tezos/Crypto/Util.hs view
@@ -30,6 +30,9 @@ | CryptoParseUnexpectedLength Builder Int deriving stock (Show, Eq) +instance NFData CryptoParseError where+ rnf = rnf @String . show+ instance Buildable CryptoParseError where build = \case
+ src/Util/CLI.hs view
@@ -0,0 +1,166 @@+-- | Utilities for command line options parsing+-- (we use @optparse-applicative@).+--+-- Some names exported from this module are quite general when if you+-- do not assume @optparse-applicative@ usage, so consider using+-- explicit imports for it.++module Util.CLI+ ( -- * General helpers+ maybeAddDefault+ , outputOption++ -- * Named and type class based parsing+ , HasCLReader (..)+ , mkCLOptionParser+ , mkCLOptionParserExt+ , mkCLArgumentParser+ , mkCLArgumentParserExt+ , namedParser++ -- ** Helpers for defining 'HasCLReader'+ , eitherReader+ , readerError+ ) where++import qualified Data.Kind as Kind+import Fmt (Buildable, pretty)+import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)+import Named (Name(..), arg)+import Options.Applicative+ (eitherReader, help, long, metavar, option, readerError, showDefaultWith, strOption, value)+import qualified Options.Applicative as Opt++import Util.Instances ()+import Util.Named++-- | Maybe add the default value and make sure it will be shown in+-- help message.+maybeAddDefault :: Opt.HasValue f => (a -> String) -> Maybe a -> Opt.Mod f a+maybeAddDefault printer = maybe mempty addDefault+ where+ addDefault v = value v <> showDefaultWith printer++-- | Parser for path to a file where output will be writen.+outputOption :: Opt.Parser (Maybe FilePath)+outputOption = optional . strOption $+ Opt.short 'o' <>+ long "output" <>+ metavar "FILEPATH" <>+ help "Write output to the given file. If not specified, stdout is used."++----------------------------------------------------------------------------+-- Named parsing+----------------------------------------------------------------------------++-- | Supporting typeclass for 'namedParser'.+-- It specifies how a value should be parsed from command line.+-- Even though the main purpose of this class is to implement+-- helpers below, feel free to use it for other goals.+class HasCLReader a where+ getReader :: Opt.ReadM a+ -- | This string will be passed to the 'metavar' function, hence we+ -- use 'String' type rather 'Text' (even though we use 'Text' almost+ -- everywhere).+ getMetavar :: String++-- Let's add instances when the need arises.+-- The downside of having 'getMetavar' is that there is no instance+-- 'HasCLReader' for 'String' (aka 'FilePath') because we want+-- different metavars for filepaths and other strings. We can define+-- it as @FILEPATH@ because we normally use 'Text' for everything+-- else, but it still sounds a bit dangerous.++instance HasCLReader Natural where+ getReader = Opt.auto+ getMetavar = "NATURAL NUMBER"++instance HasCLReader Word64 where+ getReader = Opt.auto+ -- ↓ Same as for 'Natural', the user usually does not care whether+ -- the number if bounded (reasonable values should fit anyway).+ getMetavar = "NATURAL NUMBER"++instance HasCLReader Integer where+ getReader = Opt.auto+ getMetavar = "INTEGER"++instance HasCLReader Int where+ getReader = Opt.auto+ getMetavar = "INTEGER"++instance HasCLReader Text where+ getReader = Opt.str+ getMetavar = "STRING"++-- | Create a 'Opt.Parser' for a value using 'HasCLReader' instance+-- (hence @CL@ in the name). It uses reader and metavar from that+-- class, the rest should be supplied as arguments.+--+-- We expect some common modifiers to be always provided, a list of+-- extra modifies can be provided as well.+mkCLOptionParser ::+ forall a. (Buildable a, HasCLReader a)+ => Maybe a+ -> "name" :! String+ -> "help" :! String+ -> Opt.Parser a+mkCLOptionParser defValue name hInfo =+ mkCLOptionParserExt defValue name hInfo []++-- | A more general version of 'mkCLOptionParser' which takes a list+-- of extra (not as widely used) modifiers.+mkCLOptionParserExt ::+ forall a. (Buildable a, HasCLReader a)+ => Maybe a+ -> "name" :! String+ -> "help" :! String+ -> [Opt.Mod Opt.OptionFields a]+ -> Opt.Parser a+mkCLOptionParserExt defValue (arg #name -> name) (arg #help -> hInfo) mods =+ option getReader $ mconcat $+ metavar (getMetavar @a) :+ long name :+ help hInfo :+ maybeAddDefault pretty defValue :+ mods++-- | Akin to 'mkCLOptionParser', but for arguments rather than options.+mkCLArgumentParser ::+ forall a. (Buildable a, HasCLReader a)+ => Maybe a+ -> "help" :! String+ -> Opt.Parser a+mkCLArgumentParser defValue hInfo = mkCLArgumentParserExt defValue hInfo []++-- | Akin to 'mkCLOptionParserExt', but for arguments rather than options.+mkCLArgumentParserExt ::+ forall a. (Buildable a, HasCLReader a)+ => Maybe a+ -> "help" :! String+ -> [Opt.Mod Opt.ArgumentFields a]+ -> Opt.Parser a+mkCLArgumentParserExt defValue (arg #help -> hInfo) mods =+ Opt.argument getReader $ mconcat $+ metavar (getMetavar @a) :+ help hInfo :+ maybeAddDefault pretty defValue :+ mods++-- | Create a 'Opt.Parser' for a value using its type-level name.+namedParser ::+ forall (a :: Kind.Type) (name :: Symbol).+ (Buildable a, HasCLReader a, KnownSymbol name)+ => Maybe a+ -> String+ -> Opt.Parser (name :! a)+namedParser defValue hInfo =+ option ((Name @name) <.!> getReader) $+ mconcat+ [ long name+ , metavar (getMetavar @a)+ , help hInfo+ , maybeAddDefault pretty (Name @name <.!> defValue)+ ]+ where+ name = symbolVal (Proxy @name)
src/Util/Exception.hs view
@@ -1,10 +1,20 @@ module Util.Exception- ( TextException (..)+ ( -- * Common exceptions+ TextException (..)++ -- * Better printing of exceptions+ , displayUncaughtException ) where +import Control.Exception (throwIO) import Fmt (Buildable(..), pretty)+import System.Exit (ExitCode(..)) import qualified Text.Show +----------------------------------------------------------------------------+-- Common exceptions+----------------------------------------------------------------------------+ data TextException = TextException Text instance Exception TextException @@ -13,3 +23,41 @@ instance Show TextException where show = pretty++----------------------------------------------------------------------------+-- Better printing of exceptions at executable-level+----------------------------------------------------------------------------++newtype DisplayExceptionInShow = DisplayExceptionInShow SomeException++instance Show DisplayExceptionInShow where+ show (DisplayExceptionInShow se) = displayException se++instance Exception DisplayExceptionInShow++-- | Customise default uncaught exception handling. The problem with+-- the default handler is that it uses `show` to display uncaught+-- exceptions, but `displayException` may provide more reasonable+-- output. We do not modify uncaught exception handler, but simply+-- wrap uncaught exceptions (only synchronous ones) into+-- 'DisplayExceptionInShow'.+--+-- Some exceptions (currently we are aware only of 'ExitCode') are+-- handled specially by default exception handler, so we don't wrap+-- them.+displayUncaughtException :: IO () -> IO ()+displayUncaughtException = mapIOExceptions wrapUnlessExitCode+ where+ -- We can't use `mapException` here, because it only works with+ -- exceptions inside pure values, not with `IO` exceptions.+ -- Note: it doesn't catch async exceptions.+ mapIOExceptions :: (SomeException -> SomeException) -> IO a -> IO a+ mapIOExceptions f action = action `catchAny` (throwIO . f)++ -- We don't wrap `ExitCode` because it seems to be handled specially.+ -- Application exit code depends on the value stored in `ExitCode`.+ wrapUnlessExitCode :: SomeException -> SomeException+ wrapUnlessExitCode e =+ case fromException @ExitCode e of+ Just _ -> e+ Nothing -> toException $ DisplayExceptionInShow e
+ src/Util/Label.hs view
@@ -0,0 +1,44 @@+-- | Definition of the Label type and utilities+module Util.Label+ ( -- * Definitions+ Label (..)++ -- * Utilities+ , labelToText++ -- * Re-exports+ , IsLabel (..)+ ) where++import Fmt (Buildable(..), pretty)+import GHC.OverloadedLabels (IsLabel(..))+import Text.Show(Show(..))++import Util.TypeLits++--------------------------------------------------------------------------------+-- Definitions+--------------------------------------------------------------------------------++-- | Proxy for a label type that includes the 'KnownSymbol' constraint+data Label (name :: Symbol) where+ Label :: KnownSymbol name => Label name++deriving instance Eq (Label name)++instance Show (Label name) where+ show label = "Label " <> pretty label++instance (KnownSymbol name, s ~ name) => IsLabel s (Label name) where+ fromLabel = Label++instance Buildable (Label name) where+ build Label = build $ symbolVal (Proxy @name)++--------------------------------------------------------------------------------+-- Utilities+--------------------------------------------------------------------------------++-- | Utility function to get the 'Text' representation of a 'Label'+labelToText :: Label name -> Text+labelToText = pretty
src/Util/Markdown.hs view
@@ -2,6 +2,8 @@ module Util.Markdown ( Markdown , HeaderLevel (..)+ , Anchor (..)+ , ToAnchor (..) , nextHeaderLevel , mdHeader , mdSubsection@@ -32,6 +34,23 @@ -- | Level of header, starting from 1. newtype HeaderLevel = HeaderLevel Int +-- | Anchor with given text.+newtype Anchor = Anchor { unAnchor :: Text }++instance IsString Anchor where+ -- Avoiding collision with names assigned by autodoc engine+ fromString = Anchor . fromString . ("manual-" <>)++-- | Picking anchor for various things.+class ToAnchor anchor where+ toAnchor :: anchor -> Anchor++instance ToAnchor Anchor where+ toAnchor = id++instance ToAnchor Text where+ toAnchor = Anchor+ nextHeaderLevel :: HeaderLevel -> HeaderLevel nextHeaderLevel (HeaderLevel l) = HeaderLevel (l + 1) @@ -71,16 +90,16 @@ c : s -> build (toText [c]) <> mdEscapeAnchorS s -- | Turn text into valid anchor. Human-readability is not preserved.-mdEscapeAnchor :: Text -> Markdown-mdEscapeAnchor = mdEscapeAnchorS . toString+mdEscapeAnchor :: ToAnchor anchor => anchor -> Markdown+mdEscapeAnchor = mdEscapeAnchorS . toString . unAnchor . toAnchor mdRef :: Markdown -> Markdown -> Markdown mdRef txt ref = "[" <> txt <> "](" <> ref <> ")" -mdLocalRef :: Markdown -> Text -> Markdown+mdLocalRef :: ToAnchor anchor => Markdown -> anchor -> Markdown mdLocalRef txt anchor = mdRef txt ("#" <> mdEscapeAnchor anchor) -mdAnchor :: Text -> Markdown+mdAnchor :: ToAnchor anchor => anchor -> Markdown mdAnchor name = "<a name=\"" <> mdEscapeAnchor name <> "\"></a>\n\n" mdSeparator :: Markdown@@ -105,6 +124,8 @@ "<!---\n" +| commentText |+ "\n-->" -- | Quasi quoter for Markdown.+--+-- This supports interpolation via @#{expression}@ syntax. md :: QuasiQuoter md = QuasiQuoter { quoteExp = \s -> [|fromString @Markdown $ unindent $(quoteExp Interpolate.i s) |]
src/Util/Named.hs view
@@ -10,9 +10,10 @@ , (<.?>) , ApplyNamedFunctor , NamedInner+ , KnownNamedFunctor (..) ) where -import Control.Lens (Wrapped(..), iso)+import Control.Lens (Iso', Wrapped(..), iso) import Data.Aeson (FromJSON, ToJSON) import Data.Data (Data) import qualified Data.Kind as Kind@@ -20,6 +21,7 @@ import GHC.TypeLits (KnownSymbol, symbolVal) import Named ((:!), (:?), Name, NamedF(..)) import qualified Text.Show+import Util.Label (Label) (.!) :: Name name -> a -> NamedF Identity a name (.!) _ = ArgF . Identity@@ -39,6 +41,21 @@ type family NamedInner (n :: Kind.Type) where NamedInner (NamedF f a _) = ApplyNamedFunctor f a++-- | Isomorphism between named entity and the entity itself wrapped into the+-- respective functor.+namedFL :: Label name -> Iso' (NamedF f a name) (f a)+namedFL _ = iso (\(ArgF x) -> x) ArgF++class KnownNamedFunctor f where+ -- | Isomorphism between named entity and the entity itself.+ namedL :: Label name -> Iso' (NamedF f a name) (ApplyNamedFunctor f a)++instance KnownNamedFunctor Identity where+ namedL l = namedFL l . _Wrapped'++instance KnownNamedFunctor Maybe where+ namedL l = namedFL l ---------------------------------------------------------------------------- -- Instances
src/Util/Peano.hs view
@@ -39,10 +39,16 @@ , IsLongerOrSameLength , LongerOrSameLength , RequireLongerOrSameLength++ -- * Length constraints 'Dict'ionaries+ , requireLongerThan+ , requireLongerOrSameLength ) where +import Data.Constraint (Dict (..)) import Data.Singletons (Sing, SingI(..)) import Data.Type.Bool (If)+import Data.Vinyl (Rec(..)) import Data.Vinyl.TypeLevel (Nat(..), RLength) import GHC.TypeLits (ErrorMessage(..), TypeError) import GHC.TypeNats (type (+), type (-))@@ -87,6 +93,9 @@ deriving stock instance Show (Sing (n :: Nat)) deriving stock instance Eq (Sing (n :: Nat))+instance NFData (Sing (n :: Nat)) where+ rnf SZ = ()+ rnf (SS n) = rnf n instance SingI 'Z where sing = SZ@@ -222,3 +231,27 @@ RequireLongerOrSameLength (l :: [k]) (a :: Peano) instance RequireLongerOrSameLength' l a => LongerOrSameLength l a => RequireLongerOrSameLength l a++----------------------------------------------------------------------------+-- Length constraints 'Dict'ionaries+----------------------------------------------------------------------------++requireLongerThan+ :: Rec any stk+ -> Sing n+ -> Maybe (Dict (RequireLongerThan stk n))+requireLongerThan RNil _ = Nothing+requireLongerThan (_ :& _xs) SZ = Just Dict+requireLongerThan (_ :& xs) (SS n) = do+ Dict <- requireLongerThan xs n+ return Dict++requireLongerOrSameLength+ :: Rec any stk+ -> Sing n+ -> Maybe (Dict (RequireLongerOrSameLength stk n))+requireLongerOrSameLength _ SZ = Just Dict+requireLongerOrSameLength RNil (SS _) = Nothing+requireLongerOrSameLength (_ :& xs) (SS n) = do+ Dict <- requireLongerOrSameLength xs n+ return Dict
src/Util/Positive.hs view
@@ -17,8 +17,10 @@ -- We define our own datatype in order to have 'Data' instance for it, -- which can not be derived for third-party types without exported constructor. newtype Positive = PositiveUnsafe { unPositive :: Natural }- deriving stock (Eq, Ord, Data)+ deriving stock (Eq, Ord, Data, Generic) deriving newtype (Show, Buildable, ToJSON, FromJSON)++instance NFData Positive mkPositive :: (Integral i, Buildable i) => i -> Either Text Positive mkPositive a
+ src/Util/TH.hs view
@@ -0,0 +1,40 @@+module Util.TH (deriveGADTNFData) where++import Language.Haskell.TH++-- | Generates an NFData instance for a GADT. /Note:/ This will not generate+-- additional constraints to the generated instance if those are required.+deriveGADTNFData :: Name -> Q [Dec]+deriveGADTNFData name = do+ (TyConI (DataD _ dataName vars _ cons _)) <- reify name+ let+ getNameFromVar (PlainTV n) = n+ getNameFromVar (KindedTV n _) = n+ convertTyVars orig = foldr (\a b -> AppT b . VarT $ getNameFromVar a) orig vars++ -- Unfolds multiple constructors of form "A, B, C :: A -> Stuff"+ -- into a list of tuples of constructor names and their data+ unfoldConstructor (GadtC cs bangs _) = map (,bangs) cs+ unfoldConstructor (ForallC _ _ c) = unfoldConstructor c+ unfoldConstructor _ = fail "Non GADT constructors are not supported."++ -- Constructs a clause "rnf (ConName a1 a2 ...) = rnf (a1, a2, ...)+ makeClauses (conName, bangs) = do+ varNames <- traverse (\_ -> newName "a") bangs+ let rnfExp e = AppE (VarE $ mkName "rnf") e+ return $+ (Clause+ [ConP conName $ map VarP varNames]+ (NormalB (rnfExp . TupE $ map VarE varNames))+ []+ )++ makeInstance clauses =+ InstanceD+ Nothing+ []+ (AppT (ConT $ mkName "NFData") (convertTyVars $ ConT dataName))+ [FunD (mkName "rnf") clauses]++ clauses <- traverse makeClauses $ cons >>= unfoldConstructor+ return [makeInstance clauses]
src/Util/Text.hs view
@@ -1,5 +1,6 @@ module Util.Text ( headToLower+ , surround ) where import Data.Char (toLower)@@ -12,3 +13,6 @@ headToLower txt = case T.uncons txt of Nothing -> error "Empty text" Just (c, cs) -> T.cons (toLower c) cs++surround :: Semigroup a => a -> a -> a -> a+surround pre post content = pre <> content <> post
src/Util/Type.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -Wno-redundant-constraints #-}+{-# LANGUAGE QuantifiedConstraints #-} -- | General type utilities. module Util.Type@@ -22,18 +23,22 @@ , KList (..) , RSplit , rsplit+ , Some1 (..)+ , recordToSomeList , reifyTypeEquality ) where +import Data.Constraint ((:-)(..), Dict(..)) import Data.Vinyl.Core (Rec (..))+import qualified Data.Vinyl.Functor as Vinyl+import Data.Vinyl.Recursive (recordToList, rmap) import Data.Vinyl.TypeLevel (type (++)) import qualified Data.Kind as Kind-import Data.Type.Bool (Not, type (&&), If)-import Unsafe.Coerce (unsafeCoerce)-import Data.Constraint ((:-)(..), Dict (..))+import Data.Type.Bool (type (&&), If, Not) import Data.Type.Equality (type (==))-import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))+import GHC.TypeLits (ErrorMessage(..), Symbol, TypeError)+import Unsafe.Coerce (unsafeCoerce) type family IsElem (a :: k) (l :: [k]) :: Bool where IsElem _ '[] = 'False@@ -141,3 +146,12 @@ KCons{} -> \(x :& r) -> let (x1, r1) = rsplit r in (x :& x1, r1)++-- | A value of type parametrized with /some/ type parameter.+data Some1 (f :: k -> Kind.Type) =+ forall a. Some1 (f a)++deriving stock instance (forall a. Show (f a)) => Show (Some1 f)++recordToSomeList :: Rec f l -> [Some1 f]+recordToSomeList = recordToList . rmap (Vinyl.Const . Some1)
src/Util/TypeLits.hs view
@@ -5,6 +5,7 @@ ( Symbol , KnownSymbol , AppendSymbol+ , symbolVal , symbolValT , symbolValT'
src/Util/Typeable.hs view
@@ -10,6 +10,7 @@ , eqExt , compareExt , castIgnoringPhantom+ , eqTypeIgnoringPhantom -- * Re-exports , (:~:) (..)@@ -145,3 +146,15 @@ return (coerce x) type family DummyPhantomType :: k where++-- | Match given type against another type of @* -> *@ kind without caring+-- about its type argument.+eqTypeIgnoringPhantom+ :: forall c x r.+ (Typeable x, Typeable c)+ => (forall a. Typeable a => (c a :~: x) -> Proxy a -> r) -> Maybe r+eqTypeIgnoringPhantom cont = do+ Refl.App x1Rep xArgRep <- pure $ Refl.typeRep @x+ let cRep = Refl.typeRep @c+ Refl.HRefl <- Refl.eqTypeRep x1Rep cRep+ return $ Refl.withTypeable xArgRep (cont Refl Proxy)
test/Test/Analyzer.hs view
@@ -30,7 +30,7 @@ str3 :: MText str3 = [mt|bb|] -sample :: T.Contract ('T.Tc 'CString) ('T.Tc 'CString)+sample :: T.ContractCode ('T.Tc 'CString) ('T.Tc 'CString) sample = CAR `Seq` DUP `Seq` pushStr str3 `Seq`
test/Test/Ext.hs view
@@ -13,17 +13,19 @@ import Michelson.Test (concatTestTrees, testTreesWithTypedContract) import Michelson.Test.Dummy (dummyContractEnv) import Michelson.TypeCheck (HST(..), SomeHST(..), runTypeCheck, typeCheckExt, typeCheckList)-import Michelson.Typed (CValue(..), epcPrimitive, withUType)+import Michelson.Typed (pattern AsUType, CValue(..), epcPrimitive) import qualified Michelson.Typed as T import Michelson.Untyped (CT(..), ExpandedExtInstr, ExtInstrAbstract(..), StackTypePattern(..), T(..), TyVar(..), Type(..), ann, noAnn) +import Test.Util.Contracts+ test_PRINT_and_TEST_ASSERT :: IO [TestTree] test_PRINT_and_TEST_ASSERT = concatTestTrees- [ testTreesWithTypedContract "contracts/testassert_square.mtz" $+ [ testTreesWithTypedContract (inContractsDir "testassert_square.mtz") $ testAssertSquareImpl- , testTreesWithTypedContract "contracts/testassert_square2.mtz" $+ , testTreesWithTypedContract (inContractsDir "testassert_square2.mtz") $ testAssertSquareImpl ] where@@ -43,7 +45,7 @@ let check (a, InterpreterState s _) = if corr then isRight a && s == MorleyLogs ["Area is " <> show area'] else isLeft a && s == MorleyLogs ["Sides are " <> show x' <> " x " <> show y']- interpret contract epcPrimitive (T.VPair (x', y')) T.VUnit dummyContractEnv `shouldSatisfy` check+ interpret (T.fcCode contract) epcPrimitive (T.VPair (x', y')) T.VUnit dummyContractEnv `shouldSatisfy` check test_STACKTYPE :: [TestTree] test_STACKTYPE =@@ -79,9 +81,9 @@ convertToHST :: [Type] -> SomeHST convertToHST [] = SomeHST SNil- convertToHST (t : ts) = withUType t $ \((sing :: T.Sing t), (nt :: T.Notes t)) ->+ convertToHST (AsUType nt : ts) = case convertToHST ts of- SomeHST is -> SomeHST ((sing, nt, noAnn) ::& is)+ SomeHST is -> SomeHST ((nt, noAnn) ::& is) nh (ni, si) = runTypeCheck (Type TKey noAnn) mempty $ usingReaderT def $ typeCheckExt typeCheckList ni si
test/Test/Interpreter.hs view
@@ -17,35 +17,49 @@ , test_split_string_simple , test_complex_strings , test_contract_instr_on_implicit- , test_Entry_points_lookup- , test_Entry_points_calling+ , test_map_preserve_stack ) where -import qualified Data.Map as Map-import Data.Singletons (SingI) import Fmt (pretty)+import System.FilePath ((</>)) import Test.Hspec.Expectations (Expectation, expectationFailure, shouldBe, shouldSatisfy) import Test.HUnit (assertFailure) import Test.QuickCheck (Property, label, (.&&.), (===)) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase) import Test.Tasty.QuickCheck (testProperty)-import Util.Named ((.!)) import Michelson.Interpret (ContractEnv(..), ContractReturn, MichelsonFailed(..), RemainingSteps, interpret) import Michelson.Test+ (ContractPropValidator, concatTestTrees, contractProp, testTreesWithTypedContract, validateStorageIs)+import Michelson.Test.Dummy (dummyContractEnv)+import Michelson.Test.Util (failedProp)+import Michelson.Test.Unit import Michelson.Text import Michelson.Typed (CT(..), CValue(..), IsoValue(..), T(..), epcPrimitive) import qualified Michelson.Typed as T-import qualified Michelson.Untyped as U import Tezos.Address-import Tezos.Core import Tezos.Crypto +import Test.Util.Contracts++interpretSimple+ :: forall cp st.+ ( IsoValue cp, IsoValue st+ , T.ParameterScope (ToT cp)+ , T.ForbidOr (ToT cp)+ )+ => T.FullContract (ToT cp) (ToT st)+ -> cp+ -> st+ -> ContractReturn (ToT st)+interpretSimple contract cp st =+ interpret (T.fcCode contract) epcPrimitive (toVal cp) (toVal st) dummyContractEnv+ test_basic5 :: IO [TestTree] test_basic5 =- testTreesWithTypedContract "contracts/basic5.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "basic5.tz") $ \contract -> pure [ testCase "Basic test" $ contractProp @() @[Integer] contract (validateStorageIs [13 :: Integer, 100]) dummyContractEnv () [1]@@ -53,7 +67,7 @@ test_increment :: IO [TestTree] test_increment =- testTreesWithTypedContract "contracts/increment.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "increment.tz") $ \contract -> pure [ testCase "Basic test" $ contractProp @() @Integer contract (validateStorageIs @Integer 24) dummyContractEnv () 23@@ -61,31 +75,31 @@ test_fail :: IO [TestTree] test_fail =- testTreesWithTypedContract "contracts/tezos_examples/macros/fail.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "tezos_examples/macros/fail.tz") $ \contract -> pure [ testCase "Fail test" $- interpret contract epcPrimitive T.VUnit T.VUnit dummyContractEnv+ interpretSimple contract () () `shouldSatisfy` (isLeft . fst) ] test_mutez_add_overflow :: IO [TestTree] test_mutez_add_overflow =- testTreesWithTypedContract "contracts/mutez_add_overflow.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "mutez_add_overflow.tz") $ \contract -> pure [ testCase "Mutez add overflow test" $- interpret contract epcPrimitive T.VUnit T.VUnit dummyContractEnv+ interpretSimple contract () () `shouldSatisfy` (isLeft . fst) ] test_mutez_sub_overflow :: IO [TestTree] test_mutez_sub_overflow =- testTreesWithTypedContract "contracts/mutez_sub_underflow.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "mutez_sub_underflow.tz") $ \contract -> pure [ testCase "Mutez sub underflow test" $- interpret contract epcPrimitive T.VUnit T.VUnit dummyContractEnv+ interpretSimple contract () () `shouldSatisfy` (isLeft . fst) ] test_basic1 :: IO [TestTree] test_basic1 =- testTreesWithTypedContract "contracts/basic1.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "basic1.tz") $ \contract -> pure [ testProperty "Random check" $ \input -> contractProp @_ @[Integer] contract (validateBasic1 input) dummyContractEnv () input@@ -93,36 +107,36 @@ test_lsl :: IO [TestTree] test_lsl =- testTreesWithTypedContract "contracts/lsl.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "lsl.tz") $ \contract -> pure [ testCase "LSL shouldn't overflow test" $ contractProp @Natural @Natural contract (validateStorageIs @Natural 20) dummyContractEnv 5 2 , testCase "LSL should overflow test" $- interpret contract epcPrimitive (toVal @Natural 5) (toVal @Natural 257) dummyContractEnv+ interpretSimple contract (5 :: Natural) (257 :: Natural) `shouldSatisfy` (isLeft . fst) ] test_lsr :: IO [TestTree] test_lsr =- testTreesWithTypedContract "contracts/lsr.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "lsr.tz") $ \contract -> pure [ testCase "LSR shouldn't underflow test" $ contractProp @Natural @Natural contract (validateStorageIs @Natural 3) dummyContractEnv 30 3 , testCase "LSR should underflow test" $- interpret contract epcPrimitive (toVal @Natural 1000) (toVal @Natural 257) dummyContractEnv+ interpretSimple contract (1000 :: Natural) (257 :: Natural) `shouldSatisfy` (isLeft . fst) ] test_FAILWITH :: IO [TestTree] test_FAILWITH = concatTestTrees- [ testTreesWithTypedContract "contracts/failwith_message.tz" $ \contract ->+ [ testTreesWithTypedContract (contractsDir </> "failwith_message.tz") $ \contract -> pure [ testCase "Failwith message test" $ do let msg = [mt|An error occurred.|] :: MText contractProp contract (validateMichelsonFailsWith msg) dummyContractEnv msg () ]- , testTreesWithTypedContract "contracts/failwith_message2.tz" $ \contract ->+ , testTreesWithTypedContract (contractsDir </> "failwith_message2.tz") $ \contract -> pure [ testCase "Conditional failwith message test" $ do let msg = [mt|An error occurred.|]@@ -137,26 +151,26 @@ test_STEPS_TO_QUOTA :: IO [TestTree] test_STEPS_TO_QUOTA = concatTestTrees- [ testTreesWithTypedContract "contracts/steps_to_quota_test1.tz" $ \contract ->+ [ testTreesWithTypedContract (contractsDir </> "deprecated/steps_to_quota_test1.tz") $ \contract -> pure [ testCase "Amount of steps should decrease (1)" $ do validateStepsToQuotaTest- (interpret contract epcPrimitive T.VUnit (T.VC (CvNat 0)) dummyContractEnv) 4+ (interpretSimple contract () (0 :: Natural)) 4 ]- , testTreesWithTypedContract "contracts/steps_to_quota_test2.tz" $ \contract ->+ , testTreesWithTypedContract (contractsDir </> "deprecated/steps_to_quota_test2.tz") $ \contract -> pure [ testCase "Amount of steps should decrease (2)" $ do validateStepsToQuotaTest- (interpret contract epcPrimitive T.VUnit (T.VC (CvNat 0)) dummyContractEnv) 8+ (interpretSimple contract () (0 :: Natural)) 8 ] ] test_gas_exhaustion :: IO [TestTree] test_gas_exhaustion =- testTreesWithTypedContract "contracts/gas_exhaustion.tz" $ \contract -> pure+ testTreesWithTypedContract (contractsDir </> "gas_exhaustion.tz") $ \contract -> pure [ testCase "Contract should fail due to gas exhaustion" $ do- let dummyStr = toVal [mt|x|]- case fst $ interpret contract epcPrimitive dummyStr dummyStr dummyContractEnv of+ let dummyStr = [mt|x|]+ case fst $ interpretSimple contract dummyStr dummyStr of Right _ -> assertFailure "expecting contract to fail" Left MichelsonGasExhaustion -> pass Left _ -> assertFailure "expecting another failure reason"@@ -164,7 +178,7 @@ test_add1_list :: IO [TestTree] test_add1_list =- testTreesWithTypedContract "contracts/tezos_examples/attic/add1_list.tz" $ \contract ->+ testTreesWithTypedContract (contractsDir </> "tezos_examples/attic/add1_list.tz") $ \contract -> let doValidate :: [Integer] -> ContractPropValidator (ToT [Integer]) Property@@ -181,7 +195,7 @@ test_Sum_types :: IO [TestTree] test_Sum_types = concatTestTrees- [ testTreesWithTypedContract "contracts/union.mtz" $ \contract -> pure+ [ testTreesWithTypedContract (contractsDir </> "union.mtz") $ \contract -> pure [ testGroup "union.mtz: union corresponds to Haskell types properly" $ let caseTest param = contractProp contract validateSuccess dummyContractEnv param ()@@ -193,7 +207,7 @@ , testCase "Case 5" $ caseTest (Case5 [[mt|q|]]) ] ]- , testTreesWithTypedContract "contracts/case.mtz" $ \contract -> pure+ , testTreesWithTypedContract (contractsDir </> "case.mtz") $ \contract -> pure [ testGroup "CASE instruction" $ let caseTest param expectedStorage = contractProp contract (validateStorageIs @MText expectedStorage)@@ -206,7 +220,7 @@ , testCase "Case 5" $ caseTest (Case5 $ [[mt|a|], [mt|b|]]) [mt|ab|] ] ]- , testTreesWithTypedContract "contracts/tag.mtz" $ \contract -> pure+ , testTreesWithTypedContract (contractsDir </> "tag.mtz") $ \contract -> pure [ testCase "TAG instruction" $ let expected = mconcat [[mt|unit|], [mt|o|], [mt|ab|], [mt|nat|], [mt|int|]] in contractProp contract (validateStorageIs expected) dummyContractEnv () [mt||]@@ -215,18 +229,18 @@ test_Product_types :: IO [TestTree] test_Product_types = concatTestTrees- [ testTreesWithTypedContract "contracts/access.mtz" $ \contract -> pure+ [ testTreesWithTypedContract (contractsDir </> "access.mtz") $ \contract -> pure [ testCase "ACCESS instruction" $ contractProp @Tuple1 contract validateSuccess dummyContractEnv (1, [mt|a|], Just [mt|a|], Right [mt|a|], [[mt|a|]]) () ]- , testTreesWithTypedContract "contracts/set.mtz" $ \contract -> pure+ , testTreesWithTypedContract (contractsDir </> "set.mtz") $ \contract -> pure [ testCase "SET instruction" $ let expected = (2, [mt|za|], Just [mt|wa|], Right [mt|ya|], [[mt|ab|]]) :: Tuple1 in contractProp @_ @Tuple1 contract (validateStorageIs expected) dummyContractEnv () (1, [mt|a|], Just [mt|a|], Right [mt|a|], [[mt|a|], [mt|b|]]) ]- , testTreesWithTypedContract "contracts/construct.mtz" $ \contract -> pure+ , testTreesWithTypedContract (contractsDir </> "construct.mtz") $ \contract -> pure [ testCase "CONSTRUCT instruction" $ let expected = (1, [mt|a|], Just [mt|b|], Left [mt|q|], []) :: Tuple1 in contractProp @_ @Tuple1 contract (validateStorageIs expected)@@ -236,8 +250,8 @@ test_split_bytes :: IO [TestTree] test_split_bytes =- testTreesWithTypedContract "contracts/tezos_examples/opcodes/split_bytes.tz" $ \contract ->- pure+ testTreesWithTypedContract (contractsDir </> "tezos_examples/opcodes/split_bytes.tz") $+ \contract -> pure [ testCase "splits given byte sequence into parts" $ let expected = ["\11", "\12", "\13"] :: [ByteString] in contractProp contract (validateStorageIs expected) dummyContractEnv@@ -246,7 +260,7 @@ test_split_string_simple :: IO [TestTree] test_split_string_simple =- testTreesWithTypedContract "contracts/split_string_simple.tz" $ \contract ->+ testTreesWithTypedContract (contractsDir </> "split_string_simple.tz") $ \contract -> pure [ testCase "applies SLICE instruction" $ do let@@ -270,7 +284,7 @@ test_complex_strings :: IO [TestTree] test_complex_strings =- testTreesWithTypedContract "contracts/complex_strings.tz" $ \contract ->+ testTreesWithTypedContract (contractsDir </> "complex_strings.tz") $ \contract -> pure [ testCase "ComplexString" $ contractProp contract@@ -291,8 +305,8 @@ test_contract_instr_on_implicit :: IO [TestTree] test_contract_instr_on_implicit =- testTreesWithTypedContract "contracts/contract_instr_unit.tz" $ \contractGood ->- testTreesWithTypedContract "contracts/contract_instr_nonunit.tz" $ \contractBad ->+ testTreesWithTypedContract (contractsDir </> "contract_instr_unit.tz") $ \contractGood ->+ testTreesWithTypedContract (contractsDir </> "contract_instr_nonunit.tz") $ \contractBad -> pure [ testCase "CONTRACT instruction succeeds on implicit accounts" $ contractProp contractGood validateSuccess dummyContractEnv addr ()@@ -304,106 +318,22 @@ where addr = mkKeyAddress . toPublic $ detSecretKey "sfsdfsdf" --- TODO [TM-280] Move to separate module-test_Entry_points_lookup :: IO [TestTree]-test_Entry_points_lookup =- testTreesWithTypedContract (dir <> "call1.mtz") $ \call1 ->- testTreesWithTypedContract (dir <> "call2.mtz") $ \call2 ->- testTreesWithTypedContract (dir <> "call3.mtz") $ \call3 ->- testTreesWithTypedContract (dir <> "call4.mtz") $ \call4 ->- testTreesWithTypedContract (dir <> "call5.mtz") $ \call5 ->- testTreesWithTypedContract (dir <> "call6.mtz") $ \call6 ->- testTreesWithTypedContract (dir <> "call7.mtz") $ \call7 ->+-- | This test creates a map of two items and then converts them into a list+-- with @MAP@ primitive, counting the number of items along the way.+--+-- See https://gitlab.com/morley-framework/morley/-/issues/123+test_map_preserve_stack :: IO [TestTree]+test_map_preserve_stack =+ testTreesWithTypedContract (contractsDir </> "map_preserve_stack.tz") $ \contract -> pure- [ testGroup "Calling contract without default entrypoint"- [ testCase "Calling default entrypoint refers to the root" $- checkProp call1 validateSuccess (addr "simple")- , testCase "Calling some entrypoint refers this entrypoint" $- checkProp call7 validateSuccess (addr "simple")- ]- , testGroup "Calling contract with default entrypoint"- [ testCase "Calling default entrypoint works" $- checkProp call2 validateSuccess (addr "def")- ]- , testGroup "Common failures"- [ testCase "Fails on type mismatch" $- checkProp call1 validateFailure (addr "def")- , testCase "Fails on entrypoint not found" $- checkProp call3 validateFailure (addr "simple")- ]- , testGroup "Referring entrypoints groups"- [ testCase "Can refer entrypoint group" $- checkProp call4 validateSuccess (addr "complex")- , testCase "Works with annotations" $- checkProp call5 validateSuccess (addr "complex")- , testCase "Does not work on annotations mismatch in 'contract' type argument" $- checkProp call6 validateFailure (addr "complex")- ]+ [ testCase "MAP preserves deep stack modifications (#123)" $+ contractProp @() @([Integer], Integer) contract+ (validateStorageIs @([Integer], Integer) ([257, 43], 2))+ dummyContractEnv () ([], 0) ]- where- checkProp contract validator callee =- contractProp @Address @()- contract validator- dummyContractEnv{ ceContracts = Map.fromList env }- callee ()- validateFailure = validateMichelsonFailsWith () - dir = "contracts/entrypoints/"- contractSimpleTy =- U.Type (U.TOr (U.ann "a") (U.ann "b") (U.Type U.Tint U.noAnn) (U.Type U.Tnat U.noAnn))- U.noAnn- contractComplexTy =- U.Type (U.TOr (U.ann "s") (U.ann "t") (U.Type U.Tstring U.noAnn) contractSimpleTy)- U.noAnn- contractWithDefTy =- U.Type (U.TOr (U.ann "a") (U.ann "default") (U.Type U.Tnat U.noAnn) (U.Type U.Tstring U.noAnn))- U.noAnn- addr = mkContractAddressRaw- env =- [ (mkContractHashRaw "simple", contractSimpleTy)- , (mkContractHashRaw "complex", contractComplexTy)- , (mkContractHashRaw "def", contractWithDefTy)- ]--test_Entry_points_calling :: IO [TestTree]-test_Entry_points_calling =- testTreesWithUntypedContract (dir <> "call1.mtz") $ \call1 ->- testTreesWithUntypedContract (dir <> "contract1.mtz") $ \contract1 ->- testTreesWithUntypedContract (dir <> "self1.mtz") $ \self1 ->- pure- -- TODO [TM-280]: Further use 'tOriginate' at least.- -- Currently it's not possible, in untyped -> typed conversion we loose- -- information about parameter annotations; to fix this, need to replace- -- all 'T.Contract' with 'T.FullContract'.- [ testCase "Calling some entrypoint in CONTRACT" $- integrationalTestExpectation $ do- callerRef <- originate call1 "caller" (T.untypeValue $ toVal ()) (toMutez 100)- targetRef <- originate contract1 "target" (T.untypeValue $ toVal @Integer 0) (toMutez 100)-- tTransfer (#from .! genesisAddress) (#to .! callerRef) (toMutez 1)- T.DefEpName (toVal targetRef)-- validate . Right $- tExpectStorageConst targetRef (toVal @Integer 5)-- , testCase "Calling some entrypoint in SELF" $- integrationalTestExpectation $ do- contractRef <- originate self1 "self" (T.untypeValue $ toVal @Integer 0) (toMutez 100)-- tTransfer (#from .! genesisAddress) (#to .! contractRef) (toMutez 1)- T.DefEpName (toVal $ Right @Integer ())-- validate . Right $- tExpectStorageConst contractRef (toVal @Integer 5)- ]- where- dir = "contracts/entrypoints/"- ---------------------------------------------------------------------------- -validateSuccess :: HasCallStack => ContractPropValidator st Expectation-validateSuccess (res, _) = res `shouldSatisfy` isRight- validateBasic1 :: [Integer] -> ContractPropValidator ('TList ('Tc 'CInt)) Property validateBasic1 input (Right (ops, res), _) =@@ -419,8 +349,3 @@ Right ([], T.VC (CvNat x)) -> (fromInteger . toInteger) x `shouldBe` ceMaxSteps dummyContractEnv - numOfSteps _ -> expectationFailure "unexpected contract result"--validateMichelsonFailsWith- :: (T.IsoValue v, Typeable (ToT v), SingI (ToT v))- => v -> ContractPropValidator st Expectation-validateMichelsonFailsWith v (res, _) = res `shouldBe` Left (MichelsonFailedWith $ toVal v)
test/Test/Interpreter/Apply.hs view
@@ -11,9 +11,11 @@ import Michelson.Test import Michelson.Typed +import Test.Util.Contracts+ unit_Basic :: Assertion unit_Basic = do- (_, applyContract) <- importContract "contracts/apply.tz"+ (_, applyContract) <- importContract (inContractsDir "apply.tz") let lam :: Instr '[ ToT (Integer, Integer) ] '[ ToT Integer ] lam = DUP `Seq` CAR `Seq` DIP CDR `Seq` SUB@@ -25,7 +27,7 @@ unit_Partially_applied_lambda_packed :: Assertion unit_Partially_applied_lambda_packed = do (_, partApplyContract) <-- importContract "contracts/partially-applied-lambda-packed.tz"+ importContract (inContractsDir "partially-applied-lambda-packed.tz") let expected = decodeHex "05020000000f0743035b0005034202000000020316" ?: error "Bad bytes" contractProp @() @ByteString
test/Test/Interpreter/Auction.hs view
@@ -24,6 +24,8 @@ import Tezos.Crypto (KeyHash) import Util.Test.Arbitrary (runGen) +import Test.Util.Contracts+ type Storage = (Timestamp, (Mutez, KeyHash)) type Param = KeyHash @@ -33,7 +35,7 @@ -- and QuickCheck. test_Auction :: IO [TestTree] test_Auction =- testTreesWithTypedContract "contracts/tezos_examples/attic/auction.tz" auctionTest+ testTreesWithTypedContract (inContractsDir "tezos_examples/attic/auction.tz") auctionTest where -- Test auction.tz, everything should be fine auctionTest contract =
+ test/Test/Interpreter/Balance.hs view
@@ -0,0 +1,107 @@++module Test.Interpreter.Balance+ ( test_balanceIncludesAmount+ , test_balanceIncludesAmountComplexCase+ ) where++import Test.QuickCheck (Arbitrary(..), choose, withMaxSuccess)+import Test.QuickCheck.Instances.Text ()+import Test.Tasty (TestTree)+import Test.Tasty.HUnit (testCase)+import Test.Tasty.QuickCheck (testProperty)++import Michelson.Runtime.GState+import Michelson.Test (testTreesWithUntypedContract)+import Michelson.Test.Integrational+import Michelson.Typed+import qualified Michelson.Untyped as U+import Tezos.Core++import Test.Util.Contracts++data Fixture =+ Fixture+ { fStartingBalance :: Mutez+ , fAmount :: Mutez+ } deriving stock (Show)++instance Arbitrary Fixture where+ arbitrary = do+ fStartingBalance <- unsafeMkMutez <$> choose (1000, 5000)+ fAmount <- unsafeMkMutez <$> choose (0, 1000)+ return Fixture{..}++test_balanceIncludesAmount :: IO [TestTree]+test_balanceIncludesAmount = do+ testTreesWithUntypedContract+ (inContractsDir "check_if_balance_includes_incoming_amount.tz") $+ \checker ->+ pure+ [ testProperty "BALANCE includes AMOUNT" $ withMaxSuccess 50 $+ integrationalTestProperty . scenario checker+ ]+ where+ scenario :: U.Contract -> Fixture -> IntegrationalScenario+ scenario checker Fixture{..} = do+ let result = unsafeAddMutez fStartingBalance fAmount++ address <- originate+ checker+ "checkIfBalanceIncludeAmount"+ (untypeValue $ toVal ())+ fStartingBalance++ let+ txData = TxData+ { tdSenderAddress = genesisAddress+ , tdParameter = untypeValue $ toVal result+ , tdEntrypoint = DefEpName+ , tdAmount = fAmount+ }++ transfer txData address+ validate+ $ Right+ $ expectBalance address result++test_balanceIncludesAmountComplexCase :: IO [TestTree]+test_balanceIncludesAmountComplexCase = do+ testTreesWithUntypedContract (inContractsDir "balance_test_case_a.tz") $ \contractA ->+ testTreesWithUntypedContract (inContractsDir "balance_test_case_b.tz") $ \contractB ->+ pure+ [ testCase "BALANCE returns expected value in nested calls"+ $ integrationalTestExpectation+ $ scenario contractA contractB+ ]+ where+ scenario :: U.Contract -> U.Contract -> IntegrationalScenario+ scenario contractA contractB = do+ addressA <- originate+ contractA+ "balance_test_case_a"+ (untypeValue $ toVal @[Mutez] [])+ (unsafeMkMutez 0)++ addressB <- originate+ contractB+ "balance_test_case_b"+ (untypeValue $ toVal ())+ (unsafeMkMutez 0)++ let+ txData = TxData+ { tdSenderAddress = genesisAddress+ , tdParameter = untypeValue $ toVal addressB+ , tdEntrypoint = DefEpName+ , tdAmount = unsafeMkMutez 100+ }++ transfer txData addressA++ -- A sends 30 to B, then B sends 5 back to A. A records call to BALANCE at each entry.+ -- We expect that 5 mutez sent back are included in the second call to BALANCE.+ validate+ $ Right+ $ expectStorageConst addressA+ $ untypeValue+ $ toVal [unsafeMkMutez 75, unsafeMkMutez 100]
test/Test/Interpreter/CallSelf.hs view
@@ -20,9 +20,11 @@ import Tezos.Address (Address) import Tezos.Core (unsafeMkMutez) +import Test.Util.Contracts+ test_self_caller :: IO [TestTree] test_self_caller =- testTreesWithContract "contracts/call_self_several_times.tz" $ \selfCaller ->+ testTreesWithContract (inContractsDir "call_self_several_times.tz") $ \selfCaller -> pure (testImpl selfCaller) data Fixture = Fixture@@ -54,7 +56,7 @@ type Storage = 'Tc 'CNat testImpl ::- (U.Contract, Contract Parameter Storage)+ (U.Contract, FullContract Parameter Storage) -> [TestTree] testImpl (uSelfCaller, selfCaller) = [ testCase ("With parameter 1 single execution consumes " <>
test/Test/Interpreter/Compare.hs view
@@ -16,6 +16,8 @@ import qualified Michelson.Typed as T import Tezos.Core (Mutez, unsafeMkMutez) +import Test.Util.Contracts+ type Param = (Mutez, Mutez) type ContractStorage = T.Value (ToT [Bool]) type ContractResult x@@ -25,7 +27,7 @@ -- | Spec to test compare.tz contract. test_compare :: IO [TestTree] test_compare =- testTreesWithTypedContract "contracts/tezos_examples/macros/compare.tz" $ \contract ->+ testTreesWithTypedContract (inContractsDir "tezos_examples/macros/compare.tz") $ \contract -> let contractProp' inputParam = contractProp contract (validate (mkExpected inputParam))
test/Test/Interpreter/ComparePairs.hs view
@@ -16,6 +16,8 @@ import qualified Michelson.Typed as T import Tezos.Core (Mutez, unsafeMkMutez) +import Test.Util.Contracts+ type Param = ((Mutez, Mutez), (Mutez, Mutez)) type ContractStorage = T.Value (ToT [Bool]) type ContractResult x@@ -25,7 +27,7 @@ -- | Spec to test compare.tz contract. test_compare_pairs :: IO [TestTree] test_compare_pairs =- testTreesWithTypedContract "contracts/compare_pairs.tz" $ \contract ->+ testTreesWithTypedContract (inContractsDir "compare_pairs.tz") $ \contract -> let contractProp' inputParam = contractProp contract (validate (mkExpected inputParam))
test/Test/Interpreter/Conditionals.hs view
@@ -17,6 +17,8 @@ import Michelson.Typed (CValue(..), ToT) import qualified Michelson.Typed as T +import Test.Util.Contracts+ type Param = Either MText (Maybe Integer) type ContractStorage = T.Value (ToT MText) type ContractResult x@@ -26,7 +28,7 @@ -- | Spec to test conditionals.tz contract. test_conditionals :: IO [TestTree] test_conditionals =- testTreesWithTypedContract "contracts/tezos_examples/attic/conditionals.tz" $ \contract ->+ testTreesWithTypedContract (inContractsDir "tezos_examples/attic/conditionals.tz") $ \contract -> let contractProp' inputParam = contractProp contract (validate inputParam) dummyContractEnv inputParam
test/Test/Interpreter/ContractOp.hs view
@@ -10,14 +10,16 @@ import Michelson.Interpret (ContractEnv(..), ContractReturn) import Michelson.Test (contractProp, dummyContractEnv, failedProp, testTreesWithTypedContract)-import Michelson.Typed (Contract, ToT, fromVal)+import Michelson.Typed (FullContract, ToT, fromVal) import Michelson.Untyped (CT(..), T(..), Type(..), noAnn) import Tezos.Address +import Test.Util.Contracts+ -- | Spec to test compare.tz contract. test_contract_op :: IO [TestTree] test_contract_op =- testTreesWithTypedContract "contracts/contract_op.tz" $ \contract -> pure+ testTreesWithTypedContract (inContractsDir "contract_op.tz") $ \contract -> pure [ testProperty "contract not found" $ contractProp' False [] contract , testProperty "contract found, expected parameter is int :q, actual is int :q" $@@ -45,7 +47,7 @@ validate _ (Left _, _) = failedProp "Unexpected fail in interepreter" validate _ _ = failedProp "Unexpected result of script execution" - contractProp' :: Bool -> [(ContractHash, Type)] -> Contract (ToT Address) (ToT Bool) -> Property+ contractProp' :: Bool -> [(ContractHash, Type)] -> FullContract (ToT Address) (ToT Bool) -> Property contractProp' res ctrs contract = contractProp contract
test/Test/Interpreter/EnvironmentSpec.hs view
@@ -21,11 +21,16 @@ import Tezos.Core import Tezos.Crypto (detSecretKey, toPublic) +import Test.Util.Contracts+ test_environment :: IO [TestTree] test_environment =- testTreesWithContract "contracts/environment.tz" $ \contract ->- pure [ testImpl contract- , testCase ("Default balance") $ integrationalTestExpectation testDefaultBalance ]+ testTreesWithContract (inContractsDir "deprecated/environment.tz") $+ \contract -> pure+ [ testImpl contract+ , testCase ("Default balance") $+ integrationalTestExpectation testDefaultBalance+ ] data Fixture = Fixture { fNow :: Timestamp@@ -47,7 +52,7 @@ shouldExpectFailed :: Fixture -> Bool shouldExpectFailed fixture = or- [ fBalance fixture > unsafeMkMutez 1000+ [ fBalance fixture `unsafeAddMutez` fAmount fixture > unsafeMkMutez 1000 , fNow fixture < timestampFromSeconds 100500 , fPassOriginatedAddress fixture , fAmount fixture < unsafeMkMutez 15@@ -61,7 +66,7 @@ consumedGas = 19 testImpl ::- (U.Contract, T.Contract ('Tc 'CAddress) ('Tc 'CBool))+ (U.Contract, T.FullContract ('Tc 'CAddress) ('Tc 'CBool)) -> TestTree testImpl (uEnvironment, _environment) = do testProperty description $
test/Test/Interpreter/StackRef.hs view
@@ -15,12 +15,19 @@ test_mkStackRef :: TestTree test_mkStackRef = testCase "does not segfault" $- contractProp contract (flip shouldSatisfy isRight . fst)+ contractProp fullContract (flip shouldSatisfy isRight . fst) dummyContractEnv () () where stackRef = PrintComment . one . Right $ mkStackRef @1 - contract :: Contract 'TUnit 'TUnit- contract =+ fullContract :: FullContract 'TUnit 'TUnit+ fullContract = FullContract+ { fcCode = contractCode+ , fcStoreNotes = starNotes+ , fcParamNotesSafe = starParamNotes+ }++ contractCode :: ContractCode 'TUnit 'TUnit+ contractCode = CAR `Seq` DUP `Seq` Ext (PRINT stackRef) `Seq` DROP `Seq` NIL `Seq` PAIR
test/Test/Interpreter/StringCaller.hs view
@@ -9,8 +9,8 @@ import Test.QuickCheck (withMaxSuccess) import Test.QuickCheck.Instances.Text () import Test.Tasty (TestTree)-import Test.Tasty.QuickCheck (testProperty) import Test.Tasty.HUnit (testCase)+import Test.Tasty.QuickCheck (testProperty) import Michelson.Runtime.GState import Michelson.Test (testTreesWithContract)@@ -22,15 +22,18 @@ import Tezos.Address import Tezos.Core +import Test.Util.Contracts+ test_stringCaller :: IO [TestTree] test_stringCaller =- testTreesWithContract "contracts/string_caller.tz" $ \stringCaller ->- testTreesWithContract "contracts/fail_or_store_and_transfer.tz" $ \failOrStoreAndTransfer ->- pure $ testImpl stringCaller failOrStoreAndTransfer+ testTreesWithContract (inContractsDir "string_caller.tz") $ \stringCaller ->+ testTreesWithContract (inContractsDir "fail_or_store_and_transfer.tz") $+ \failOrStoreAndTransfer ->+ pure $ testImpl stringCaller failOrStoreAndTransfer testImpl ::- (U.Contract, T.Contract ('Tc 'CString) ('Tc 'CAddress))- -> (U.Contract, T.Contract ('Tc 'CString) ('Tc 'CString))+ (U.Contract, T.FullContract ('Tc 'CString) ('Tc 'CAddress))+ -> (U.Contract, T.FullContract ('Tc 'CString) ('Tc 'CString)) -> [TestTree] testImpl (uStringCaller, _stringCaller) (uFailOrStore, _failOrStoreAndTransfer) = let scenario = integrationalScenario uStringCaller uFailOrStore@@ -38,7 +41,7 @@ "stringCaller calls failOrStoreAndTransfer and updates its storage with " suffix = " and properly updates balances. But fails if failOrStoreAndTransfer's"- <> " balance is ≥ 1000 and NOW is ≥ 500"+ <> " balance is ≥ 1300 and NOW is ≥ 500" in [ testCase (prefix <> "a constant" <> suffix) $ integrationalTestExpectation (scenario constStr)@@ -103,7 +106,7 @@ transferToStringCaller -- This time execution should fail, because failOrStoreAndTransfer should fail- -- because its balance is greater than 1000.+ -- because its balance is greater than 1300. void $ validate (Left $ expectMichelsonFailed (const True) failOrStoreAndTransferAddress) -- We can also send tokens from failOrStoreAndTransfer to tz1 address directly
test/Test/Michelson/Runtime.hs view
@@ -6,6 +6,7 @@ import Control.Lens (at) import Fmt (pretty)+import System.FilePath ((</>)) import Test.Hspec.Expectations (Expectation, expectationFailure, shouldSatisfy) import Test.HUnit (Assertion, assertFailure, (@?=)) import Test.Tasty (TestTree, testGroup)@@ -25,10 +26,12 @@ import Tezos.Address import Tezos.Core (unsafeMkMutez) +import Test.Util.Contracts (contractsDir)+ test_executorPure :: IO [TestTree] test_executorPure = do illTypedContract <-- prepareContract (Just "contracts/ill-typed/sum_strings.tz")+ prepareContract (Just (contractsDir </> "ill-typed/sum_strings.tz")) pure [ testGroup "Updates storage value of executed contract" $ [ testCase "contract1" $ updatesStorageValue contractAux1
test/Test/OpSize.hs view
@@ -184,12 +184,14 @@ , typeTestCase "lambda operation int" 12 , typeTestCase "pair int int" 12+ , typeTestCase "pair (int : %) int" 12 , typeTestCase "pair (int :a) int" 18 , typeTestCase "pair (int %a) int" 18 , typeTestCase "pair (int %a :a) int" 21 , typeTestCase "pair (int %a) (int %a)" 24 , typeTestCase "pair :a (int %a) (int %a)" 30 , typeTestCase "pair :a int (int %a)" 24+ , typeTestCase "pair : (int %a :) (int %a)" 24 , typeTestCase "pair :a (int %a :a) (int %a)" 33 ] where@@ -202,6 +204,7 @@ , customInstrTestCase "FAIL" 9 , instrTestCase "DUP" 4+ , instrTestCase "DUP @" 4 , instrTestCase "DUP @a" 10 , instrTestCase "DUP; DROP" 6 , instrTestCase "DUP; SWAP" 6@@ -210,7 +213,7 @@ , instrTestCase "PUSH @a int 0" 14 , instrTestCase "PUSH @a (int :a) 0" 20 , instrTestCase "SOME" 4- , instrTestCase "SOME @a" 10+ , instrTestCase "SOME @" 4 , instrTestCase "SOME @a" 10 , instrTestCase "NONE int" 6 , instrTestCase "NONE (option int)" 8@@ -218,9 +221,12 @@ , instrTestCase "UNIT" 4 , instrTestCase "DUP; SOME; IF_NONE {}{DROP}" 20 , instrTestCase "DUP; DUP; PAIR" 8+ , instrTestCase "DUP; DUP; PAIR % % : @" 8 , instrTestCase "DUP; DUP; PAIR %a" 14 , instrTestCase "DUP; DUP; PAIR %a %a" 17 , instrTestCase "DUP; DUP; PAIR %a %a :a" 20+ , instrTestCase "DUP; DUP; PAIR %a % :a @a" 20+ , instrTestCase "DUP; DUP; PAIR % %a :a @a" 23 , instrTestCase "DUP; DUP; PAIR %a %a :a @a" 23 -- Further skipping some instructions since everything is trivial , instrTestCase "EMPTY_BIG_MAP :a @a (int :a) (unit :a)" 29
test/Test/Optimizer.hs view
@@ -4,10 +4,14 @@ ( unit_Optimize_DROP_n , unit_Optimize_DIP_n , unit_Redundant_DIP+ , unit_Adjacent_DIPs+ , unit_Nested_adjacent_DIPs , unit_UNPAIR_DROP+ , unit_Specific_PUSH , unit_Optimize_PUSH_PACK , unit_Optimizer_Tree_Independence , unit_Sample_optimize+ , unit_Pair_Unpair ) where import Prelude hiding (EQ)@@ -54,12 +58,33 @@ optimize @Stack1Int @Stack1 (DIP UNIT `Seq` DROP) @?= (DROP `Seq` UNIT) optimize @Stack1Int @Stack2UnitInt (UNIT `Seq` DIP (DUP `Seq` MUL)) @?= (DUP `Seq` MUL `Seq` UNIT) +unit_Adjacent_DIPs :: Assertion+unit_Adjacent_DIPs = do+ optimize (DIP (PUSH strValue) `Seq` DIP (PUSH strValue)) @?= (DIP (PUSH strValue `Seq` PUSH strValue))+ optimize+ (DIP (PUSH strValue) `Seq`+ DIP UNIT `Seq`+ DIP PAIR)+ @?= (DIP (PUSH strValue `Seq` UNIT `Seq` PAIR))++unit_Nested_adjacent_DIPs :: Assertion+unit_Nested_adjacent_DIPs = do+ optimize+ (IF_NONE (PUSH strValue) (DIP (PUSH strValue) `Seq` DIP (PUSH strValue) `Seq` DIP CONCAT `Seq` CONCAT))+ @?= (IF_NONE (PUSH strValue) (DIP (PUSH strValue `Seq` PUSH strValue `Seq` CONCAT) `Seq` CONCAT))+ unit_UNPAIR_DROP :: Assertion unit_UNPAIR_DROP = do -- The left is `unpair # DROP` which is essentially just `cdr`, -- but we do not optimize it fully yet (we only remove redundant DIP). optimize @Stack1Pair @Stack1 (DUP `Seq` CAR `Seq` DIP CDR `Seq` DROP) @?= (DUP `Seq` CAR `Seq` DROP `Seq` CDR) +unit_Specific_PUSH :: Assertion+unit_Specific_PUSH = do+ optimize (PUSH (T.VMap @'CInt @'T.TUnit mempty)) @?= EMPTY_MAP+ optimize (PUSH (T.VSet @'CInt mempty) `Seq` NOW) @?= (EMPTY_SET `Seq` NOW)+ optimize (PUSH T.VUnit) @?= UNIT+ unit_Optimize_PUSH_PACK :: Assertion unit_Optimize_PUSH_PACK = optimize'@@ -73,13 +98,19 @@ unit_Sample_optimize :: Assertion unit_Sample_optimize = optimize nonOptimal @?= expectedOptimized +unit_Pair_Unpair :: Assertion+unit_Pair_Unpair =+ optimize+ (PAIR `Seq` UNPAIR `Seq` UNPAIR `Seq` PAIR) @?=+ Nop+ str :: MText str = [mt|aa|] strValue :: T.Value ('T.Tc 'CString) strValue = T.VC $ T.CvString str -nonOptimal :: T.Contract ('T.Tc 'CString) ('T.Tc 'CString)+nonOptimal :: T.ContractCode ('T.Tc 'CString) ('T.Tc 'CString) nonOptimal = CAR `Seq` -- `PUSH; DROP` is erased@@ -116,7 +147,7 @@ infixr 1 #<# -- Expected output of the optimizer.-expectedOptimized :: T.Contract ('T.Tc 'CString) ('T.Tc 'CString)+expectedOptimized :: T.ContractCode ('T.Tc 'CString) ('T.Tc 'CString) expectedOptimized = CAR #<# DUP #<#
test/Test/Preprocess.hs view
@@ -31,7 +31,7 @@ str4 = [mt|naiks|] str5 = [mt|eek|] -sample :: T.Contract ('T.Tc 'CString) ('T.Tc 'CString)+sample :: T.ContractCode ('T.Tc 'CString) ('T.Tc 'CString) sample = CAR `Seq` PUSH (toStr str1) `Seq`@@ -44,7 +44,7 @@ where toStr = T.VC . T.CvString -expected :: T.Contract ('T.Tc 'CString) ('T.Tc 'CString)+expected :: T.ContractCode ('T.Tc 'CString) ('T.Tc 'CString) expected = CAR `Seq` PUSH (toStr str5) `Seq`
test/Test/Printer/Michelson.hs view
@@ -6,6 +6,7 @@ , unit_PrettyPrint , unit_PrintTypedNotes , unit_PrintSmartParens+ , unit_PrintBigDipN ) where import qualified Data.Map.Lazy as Map@@ -42,17 +43,22 @@ unit_PrintTypedNotes :: Assertion unit_PrintTypedNotes = do- contracts <- getContractsWithReferences ".tz" "contracts/notes-in-typed-contracts" "ref"+ contracts <- getContractsWithReferences ".tz" (inContractsDir "notes-in-typed-contracts") "ref" mapM_ printerTest contracts unit_PrintSmartParens :: Assertion unit_PrintSmartParens = do- contracts <- getContractsWithReferences ".tz" "contracts/smart-parens" "ref"+ contracts <- getContractsWithReferences ".tz" (inContractsDir "smart-parens") "ref" mapM_ printerTest contracts +unit_PrintBigDipN :: Assertion+unit_PrintBigDipN = do+ contracts <- getContractsWithReferences ".tz" (inContractsDir "big-dip") "ref"+ mapM_ printerTest contracts+ unit_PrettyPrint :: Assertion unit_PrettyPrint = do- contracts <- getContractsWithReferences ".tz" "contracts/pretty" "pretty"+ contracts <- getContractsWithReferences ".tz" (inContractsDir "pretty") "pretty" mapM_ prettyTest contracts where prettyTest :: (FilePath, FilePath) -> Assertion@@ -96,7 +102,7 @@ unit_let_macro :: Assertion unit_let_macro = do- let filePath = "contracts/ill-typed/letblock_trivial.mtz"+ let filePath = inContractsDir "ill-typed/letblock_trivial.mtz" contract <- printAndParse filePath =<< importUntypedContract filePath let ops = concatMap U.flattenExpandedOp (U.code contract) ops @?= [U.CDR U.noAnn U.noAnn, U.UNIT U.noAnn U.noAnn, U.DROP]
test/Test/Serialization/Michelson.hs view
@@ -168,7 +168,7 @@ | length s < 60 = s | otherwise = take 60 s <> " ..." initTypeCheckST = error "Type check state is not defined"- initStack = (sing @inp, starNotes, noAnn) ::& SNil+ initStack = (starNotes @inp, noAnn) ::& SNil unpackNegSpec :: forall (t :: T).@@ -641,6 +641,11 @@ -- ADDRESS goes below , "CHAIN_ID; DROP" ~: "0x05020000000403750320"+ -- DUP macro+ , "PUSH nat 1; PUSH nat 1; DUP 2; DROP; DROP; DROP"+ ~: "0x050200000022074303620001074303620001020000000b051f02000000020321034c032003200320"+ , "PUSH nat 1; PUSH nat 1; DUUP; DROP; DROP; DROP"+ ~: "0x050200000022074303620001074303620001020000000b051f02000000020321034c032003200320" ] parsePackSpec @'TUnit @'TUnit "arith instr"
test/Test/Typecheck.hs view
@@ -41,7 +41,7 @@ import Tezos.Core (Timestamp) import Util.IO (readFileUtf8) -import Test.Util.Contracts (getIllTypedContracts, getWellTypedContracts)+import Test.Util.Contracts (getIllTypedContracts, getWellTypedContracts, inContractsDir) unit_Good_contracts :: Assertion unit_Good_contracts = mapM_ (\f -> checkFile [] True f (const pass)) =<< getWellTypedContracts@@ -55,23 +55,23 @@ test_srcPosition :: [TestTree] test_srcPosition = [ testProperty "Verify instruction position in a typecheck error" $ do- checkIllFile "contracts/ill-typed/basic3.tz" $ \case+ checkIllFile (inContractsDir "ill-typed/basic3.tz") $ \case TCFailedOnInstr (Un.CONS _) _ _ (IsSrcPos 4 6) (Just (AnnError _)) -> True _ -> False - checkIllFile "contracts/ill-typed/testassert_invalid_stack3.mtz" $ \case+ checkIllFile (inContractsDir "ill-typed/testassert_invalid_stack3.mtz") $ \case TCFailedOnInstr Un.DROP _ _ (IsSrcPos 6 17) Nothing -> True _ -> False - checkIllFile "contracts/ill-typed/testassert_invalid_stack2.mtz" $ \case+ checkIllFile (inContractsDir "ill-typed/testassert_invalid_stack2.mtz") $ \case TCExtError _ (IsSrcPos 5 2) (TestAssertError _) -> True _ -> False - checkIllFile "contracts/ill-typed/macro_in_let_fail.mtz" $ \case+ checkIllFile (inContractsDir "ill-typed/macro_in_let_fail.mtz") $ \case TCFailedOnInstr (Un.COMPARE _) _ _ (InstrCallStack [LetName "cmpLet"] (SrcPos (Pos 3) (Pos 6))) (Just (TypeEqError _ _)) -> True _ -> False- checkIllFile "contracts/ill-typed/compare_annotation_mismatch.tz" $ \case+ checkIllFile (inContractsDir "ill-typed/compare_annotation_mismatch.tz") $ \case TCFailedOnInstr (Un.COMPARE _) _ _ _ (Just (AnnError _)) -> True _ -> False ]@@ -106,7 +106,7 @@ unit_Unreachable_code :: Assertion unit_Unreachable_code = do- let file = "contracts/ill-typed/fail_before_nop.tz"+ let file = inContractsDir "ill-typed/fail_before_nop.tz" let ics = InstrCallStack [] (srcPos 3 13) econtract <- readContract @'T.TUnit @'T.TUnit file <$> readFileUtf8 file econtract @?= Left (ICETypeCheck $ TCUnreachableCode ics (one $ Un.WithSrcEx ics $ Un.SeqEx []))@@ -130,7 +130,7 @@ property $ \(val :: T.Value (T.ToT a)) -> let uval = T.untypeValue val runTC = runTypeCheckIsolated . usingReaderT (def @InstrCallStack)- in case runTC $ typeVerifyValue uval of+ in case runTC $ typeCheckValue uval of Right got -> got @?= val Left err -> expectationFailure $ "Type check unexpectedly failed: " <> pretty err@@ -161,7 +161,7 @@ ] where printStRef i = Un.EXT . Un.UPRINT $ Un.PrintComment [Right (Un.StackRef i)]- stackEl = (sing @'T.TUnit, T.starNotes, noAnn)+ stackEl = (T.starNotes @'T.TUnit, noAnn) test_TCTypeError_display :: [TestTree] test_TCTypeError_display =@@ -193,21 +193,21 @@ untypedValue = Un.ValueSeq $ Un.ValueInt <$> l typedValue = T.VList $ (T.VC . T.CvInt) <$> toList l runTypeCheckInstr = runTypeCheckIsolated . usingReaderT def- in runTypeCheckInstr (typeVerifyValue untypedValue) == Right typedValue+ in runTypeCheckInstr (typeCheckValue untypedValue) == Right typedValue test_SELF :: [TestTree] test_SELF = [ testCase "Entrypoint not present" $- checkFile [] False "contracts/ill-typed/self-bad-entrypoint.mtz" $ \case+ checkFile [] False (inContractsDir "ill-typed/self-bad-entrypoint.mtz") $ \case TCFailedOnInstr Un.SELF{} _ _ _ (Just EntryPointNotFound{}) -> pass other -> assertFailure $ "Unexpected error: " <> pretty other , testCase "Entrypoint type mismatch" $- checkFile [] False "contracts/ill-typed/self-entrypoint-type-mismatch.mtz"+ checkFile [] False (inContractsDir "ill-typed/self-entrypoint-type-mismatch.mtz") (const pass) , testCase "Entrypoint can be found" $- checkFile [] True "contracts/entrypoints/self1.mtz"+ checkFile [] True (inContractsDir "entrypoints/self1.mtz") (const pass) ] @@ -242,8 +242,8 @@ ] doTypeCheckVal- :: (Typeable t, SingI t)+ :: SingI t => TcOriginatedContracts -> Un.Value -> Either TCError (T.Value t) doTypeCheckVal env uval = runTypeCheck (error "hello there") env $ usingReaderT def $- typeVerifyValue uval+ typeCheckValue uval
test/Test/Util/Contracts.hs view
@@ -1,29 +1,41 @@ -- | Utility functions to read sample contracts (for testing). module Test.Util.Contracts- ( getIllTypedContracts- , getWellTypedContracts- , getUnparsableContracts- , getWellTypedMichelsonContracts- , getWellTypedMorleyContracts- , getContractsWithReferences- ) where+ ( contractsDir+ , inContractsDir+ , (</>) + , getIllTypedContracts+ , getWellTypedContracts+ , getUnparsableContracts+ , getWellTypedMichelsonContracts+ , getWellTypedMorleyContracts+ , getContractsWithReferences+ ) where+ import Data.List (isSuffixOf) import System.Directory (listDirectory) import System.FilePath (addExtension, (</>)) +-- | Directory with sample contracts.+contractsDir :: FilePath+contractsDir = "../../contracts/"++inContractsDir :: FilePath -> FilePath+inContractsDir = (contractsDir </>)+ getIllTypedContracts :: IO [FilePath] getIllTypedContracts =- concatMapM (flip getContractsWithExtension "contracts/ill-typed")- [".tz", ".mtz"]+ concatMapM (\ext ->+ concatMapM (getContractsWithExtension ext) illTypedContractDirs+ ) [".tz", ".mtz"] getWellTypedContracts :: IO [FilePath] getWellTypedContracts = getWellTypedMichelsonContracts <> getWellTypedMorleyContracts getUnparsableContracts :: IO [FilePath] getUnparsableContracts =- concatMapM (flip getContractsWithExtension "contracts/unparsable")+ concatMapM (flip getContractsWithExtension (contractsDir </> "unparsable")) [".tz", ".mtz"] getWellTypedMichelsonContracts :: IO [FilePath]@@ -44,13 +56,21 @@ wellTypedContractDirs :: [FilePath] wellTypedContractDirs =- "contracts" :- map (("contracts" </> "tezos_examples") </>)+ contractsDir :+ map ((contractsDir </> "tezos_examples") </>) [ "attic"+ -- , "entrypoints"+ -- ^ TODO: uncomment once https://gitlab.com/morley-framework/morley/issues/35 is resolved , "macros" , "mini_scenarios" , "opcodes" ]++illTypedContractDirs :: [FilePath]+illTypedContractDirs =+ [ contractsDir </> "ill-typed"+ , contractsDir </> "tezos_examples" </> "ill_typed"+ ] getContractsWithReferences :: String -> FilePath -> String -> IO [(FilePath, FilePath)] getContractsWithReferences ext fp refExt =