morley 1.4.0 → 1.5.0
raw patch · 112 files changed
+2834/−9663 lines, 112 filesdep +generic-derivingdep +haskelinedep −HUnitdep −base16-bytestringdep −criteriondep ~morley-preludePVP ok
version bump matches the API change (PVP)
Dependencies added: generic-deriving, haskeline
Dependencies removed: HUnit, base16-bytestring, criterion, directory, filepath, formatting, gauge, ghc-prim, hedgehog, hspec, hspec-expectations, o-clock, statistics, tasty, tasty-ant-xml, tasty-hedgehog, tasty-hspec, tasty-hunit-compat, tasty-quickcheck
Dependency ranges changed: morley-prelude
API changes (from Hackage documentation)
- Michelson.Doc: -- Documentation structure is not necessarily flat. If some doc item
- Michelson.Doc: -- consolidates a whole documentation block within it, this block will
- Michelson.Doc: -- doc.
- Michelson.Doc: -- value, the higher the section with this element will be placed.
- Michelson.Doc.Test: DocTest :: SrcLoc -> String -> (HasCallStack => ContractDoc -> Assertion) -> DocTest
- Michelson.Doc.Test: [dtDeclLoc] :: DocTest -> SrcLoc
- Michelson.Doc.Test: [dtDesc] :: DocTest -> String
- Michelson.Doc.Test: [dtSuite] :: DocTest -> HasCallStack => ContractDoc -> Assertion
- Michelson.Doc.Test: data DocTest
- Michelson.Doc.Test: excludeDocTest :: HasCallStack => DocTest -> [DocTest] -> [DocTest]
- Michelson.Doc.Test: excludeDocTests :: [DocTest] -> [DocTest] -> [DocTest]
- Michelson.Doc.Test: expectDocTestFailure :: DocTest -> ContractDoc -> Assertion
- Michelson.Doc.Test: forEachContractDocItem :: DocItem d => ContractDoc -> (d -> r) -> [r]
- Michelson.Doc.Test: forEachContractLayer :: ContractDoc -> (Maybe SomeDocItem -> DocBlock -> r) -> [r]
- Michelson.Doc.Test: instance Formatting.Buildable.Buildable Michelson.Doc.Test.DocTest
- Michelson.Doc.Test: instance GHC.Classes.Eq Michelson.Doc.Test.DocTest
- Michelson.Doc.Test: instance GHC.Show.Show Michelson.Doc.Test.DocTest
- Michelson.Doc.Test: mkDocTest :: HasCallStack => String -> (HasCallStack => ContractDoc -> Assertion) -> DocTest
- Michelson.Doc.Test: runDocTests :: HasCallStack => [DocTest] -> ContractDoc -> [TestTree]
- Michelson.Doc.Test: testContractNameAtTop :: DocTest
- Michelson.Doc.Test: testDocBasic :: [DocTest]
- Michelson.Doc.Test: testDocNotEmpty :: DocTest
- Michelson.Doc.Test: testNoAdjacentDescriptions :: DocTest
- Michelson.Doc.Test: testNoGitInfo :: DocTest
- Michelson.Doc.Test: testStorageIsDocumented :: DocTest
- Michelson.Macro: mapLeaves :: [(VarAnn, FieldAnn)] -> PairStruct -> PairStruct
- Michelson.Runtime: [csTypedContract] :: ContractState -> Maybe SomeContract
- Michelson.Runtime: [csTypedStorage] :: ContractState -> Maybe SomeValue
- Michelson.Runtime: typeCheckWithDb :: FilePath -> Contract -> IO (Either TCError SomeContract)
- Michelson.Runtime.GState: GSAddAddress :: Address -> AddressState -> GStateUpdate
- Michelson.Runtime.GState: GSIncrementCounter :: GStateUpdate
- Michelson.Runtime.GState: GSSetBalance :: Address -> Mutez -> GStateUpdate
- Michelson.Runtime.GState: GSSetStorageValue :: Address -> Value -> SomeValue -> GStateUpdate
- Michelson.Runtime.GState: SomeContractAndStorage :: Contract cp st -> Value st -> SomeContractAndStorage
- Michelson.Runtime.GState: [csTypedContract] :: ContractState -> Maybe SomeContract
- Michelson.Runtime.GState: [csTypedStorage] :: ContractState -> Maybe SomeValue
- Michelson.Runtime.GState: [scsContract] :: SomeContractAndStorage -> Contract cp st
- Michelson.Runtime.GState: [scsStorage] :: SomeContractAndStorage -> Value st
- Michelson.Runtime.GState: data SomeContractAndStorage
- Michelson.Runtime.GState: getTypedContract :: GState -> ContractState -> Either TCError SomeContract
- Michelson.Runtime.GState: getTypedContractAndStorage :: (TCError -> err) -> (TCError -> err) -> GState -> ContractState -> Either err SomeContractAndStorage
- Michelson.Runtime.GState: getTypedStorage :: GState -> ContractState -> Either TCError SomeValue
- Michelson.Test: (?-) :: Text -> a -> (Text, a)
- Michelson.Test: TxData :: Address -> Value -> EpName -> Mutez -> TxData
- Michelson.Test: [tdAmount] :: TxData -> Mutez
- Michelson.Test: [tdEntrypoint] :: TxData -> EpName
- Michelson.Test: [tdParameter] :: TxData -> Value
- Michelson.Test: [tdSenderAddress] :: TxData -> Address
- Michelson.Test: attempt :: IntegrationalScenarioM a -> IntegrationalScenarioM (Either ExecutorError a)
- Michelson.Test: branchout :: HasCallStack => [(Text, IntegrationalScenario)] -> IntegrationalScenario
- Michelson.Test: catchExpectedError :: IntegrationalScenarioM a -> (ExecutorError -> IntegrationalScenarioM b) -> IntegrationalScenarioM b
- Michelson.Test: concatTestTrees :: [IO [TestTree]] -> IO [TestTree]
- Michelson.Test: contractProp :: (IsoValue param, IsoValue storage, ToT param ~ cp, ToT storage ~ st, ParameterScope cp) => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> param -> storage -> prop
- Michelson.Test: contractPropVal :: ParameterScope cp => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> Value cp -> Value st -> prop
- Michelson.Test: data TxData
- Michelson.Test: dummyContractEnv :: ContractEnv
- Michelson.Test: eitherIsLeft :: (Show b, MonadTest m, HasCallStack) => Either a b -> m ()
- Michelson.Test: eitherIsRight :: (Show a, MonadTest m, HasCallStack) => Either a b -> m ()
- Michelson.Test: excludeDocTests :: [DocTest] -> [DocTest] -> [DocTest]
- Michelson.Test: expectBalance :: Address -> Mutez -> IntegrationalScenario
- Michelson.Test: expectError :: IntegrationalScenarioM a -> IntegrationalScenarioM ExecutorError
- Michelson.Test: expectGasExhaustion :: ExecutorError -> IntegrationalScenario
- Michelson.Test: expectMichelsonFailed :: (MichelsonFailed -> Bool) -> Address -> ExecutorError -> IntegrationalScenario
- Michelson.Test: expectNoStorageUpdates :: IntegrationalScenario
- Michelson.Test: expectNoUpdates :: IntegrationalScenario
- Michelson.Test: expectStorage :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- Michelson.Test: expectStorageConst :: Address -> Value -> IntegrationalScenario
- Michelson.Test: expectStorageUpdate :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- Michelson.Test: expectStorageUpdateConst :: Address -> Value -> IntegrationalScenario
- Michelson.Test: failedProp :: Text -> Property
- Michelson.Test: failedTest :: (HasCallStack, MonadTest m) => Text -> m ()
- Michelson.Test: genesisAddress :: Address
- Michelson.Test: importContract :: forall cp st. Each '[KnownT] [cp, st] => FilePath -> IO (Contract, Contract cp st)
- Michelson.Test: importUntypedContract :: FilePath -> IO Contract
- Michelson.Test: infixr 0 ?-
- Michelson.Test: integrationalFail :: TestError -> IntegrationalScenarioM anything
- Michelson.Test: integrationalTestExpectation :: HasCallStack => IntegrationalScenario -> Expectation
- Michelson.Test: integrationalTestProp :: MonadTest m => IntegrationalScenario -> m ()
- Michelson.Test: integrationalTestProperty :: IntegrationalScenario -> Property
- Michelson.Test: maxTimestamp :: Timestamp
- Michelson.Test: mcs :: RatioNat -> Time Microsecond
- Michelson.Test: meanTimeUpperBoundProp :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack) => Time unit -> (a -> b) -> a -> Property
- Michelson.Test: meanTimeUpperBoundPropNF :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack, NFData b) => Time unit -> (a -> b) -> a -> Property
- Michelson.Test: midTimestamp :: Timestamp
- Michelson.Test: minTimestamp :: Timestamp
- Michelson.Test: minute :: RatioNat -> Time Minute
- Michelson.Test: ms :: RatioNat -> Time Millisecond
- Michelson.Test: originate :: Contract -> Text -> Value -> Mutez -> IntegrationalScenarioM Address
- Michelson.Test: qcIsLeft :: Show b => Either a b -> Property
- Michelson.Test: qcIsRight :: Show a => Either a b -> Property
- Michelson.Test: rewindTime :: Integer -> IntegrationalScenarioM ()
- Michelson.Test: runDocTests :: HasCallStack => [DocTest] -> ContractDoc -> [TestTree]
- Michelson.Test: sec :: RatioNat -> Time Second
- Michelson.Test: setChainId :: ChainId -> IntegrationalScenarioM ()
- Michelson.Test: setMaxSteps :: RemainingSteps -> IntegrationalScenarioM ()
- Michelson.Test: setNow :: Timestamp -> IntegrationalScenarioM ()
- Michelson.Test: specWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> Spec) -> Spec
- Michelson.Test: specWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> Spec) -> Spec
- Michelson.Test: specWithUntypedContract :: FilePath -> (Contract -> Spec) -> Spec
- Michelson.Test: succeededProp :: Property
- Michelson.Test: succeededTest :: MonadTest m => m ()
- Michelson.Test: tExpectStorageConst :: forall st. StorageScope st => Address -> Value st -> IntegrationalScenario
- Michelson.Test: tOriginate :: (ParameterScope cp, StorageScope st) => Contract cp st -> Text -> Value st -> Mutez -> IntegrationalScenarioM Address
- Michelson.Test: tTransfer :: forall arg. ParameterScope arg => ("from" :! Address) -> ("to" :! Address) -> Mutez -> EpName -> Value arg -> IntegrationalScenarioM ()
- Michelson.Test: testDocBasic :: [DocTest]
- Michelson.Test: testTreesWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test: testTreesWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test: testTreesWithUntypedContract :: HasCallStack => FilePath -> (Contract -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test: total :: (MonadTest m, NFData a, HasCallStack) => a -> m a
- Michelson.Test: transfer :: TxData -> Address -> IntegrationalScenarioM ()
- Michelson.Test: type ContractPropValidator st prop = ContractReturn st -> prop
- Michelson.Test: type ContractReturn st = (Either MichelsonFailed ([Operation], Value st), InterpreterState)
- Michelson.Test: type IntegrationalScenario = IntegrationalScenarioM ()
- Michelson.Test: type IntegrationalScenarioM = StateT InternalState (Except ScenarioError)
- Michelson.Test: unexpectedInterpreterError :: ExecutorError -> Text -> IntegrationalScenarioM a
- Michelson.Test: validateMichelsonFailsWith :: IsoValue v => v -> ContractPropValidator st Expectation
- Michelson.Test: validateStorageIs :: IsoValue st => st -> ContractPropValidator (ToT st) Assertion
- Michelson.Test: validateSuccess :: HasCallStack => ContractPropValidator st Expectation
- Michelson.Test: withSender :: Address -> IntegrationalScenarioM a -> IntegrationalScenarioM a
- Michelson.Test.Dummy: dummyContractEnv :: ContractEnv
- Michelson.Test.Dummy: dummyMaxSteps :: RemainingSteps
- Michelson.Test.Dummy: dummyNow :: Timestamp
- Michelson.Test.Dummy: dummyOrigination :: Value -> Contract -> OriginationOperation
- Michelson.Test.Gen: genMutez :: MonadGen m => m Mutez
- Michelson.Test.Gen: genTimestamp :: MonadGen m => m Timestamp
- Michelson.Test.Gen: genValueInt :: MonadGen m => m (Value' instr 'TInt)
- Michelson.Test.Gen: genValueKeyHash :: MonadGen m => m (Value' instr 'TKeyHash)
- Michelson.Test.Gen: genValueList :: (MonadGen m, KnownT a) => m (Value' instr a) -> m (Value' instr ('TList a))
- Michelson.Test.Gen: genValueMutez :: MonadGen m => m (Value' instr 'TMutez)
- Michelson.Test.Gen: genValuePair :: MonadGen m => m (Value' instr a) -> m (Value' instr b) -> m (Value' instr ('TPair a b))
- Michelson.Test.Gen: genValueTimestamp :: MonadGen m => m (Value' instr 'TTimestamp)
- Michelson.Test.Gen: genValueUnit :: Applicative m => m (Value' instr 'TUnit)
- Michelson.Test.Gen: instance (Michelson.Typed.Sing.KnownT a, Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr a)) => Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr ('Michelson.Typed.T.TList a))
- Michelson.Test.Gen: instance (Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr a), Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr b)) => Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr ('Michelson.Typed.T.TPair a b))
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr 'Michelson.Typed.T.TInt)
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr 'Michelson.Typed.T.TKeyHash)
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr 'Michelson.Typed.T.TMutez)
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr 'Michelson.Typed.T.TTimestamp)
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary (Michelson.Typed.Value.Value' instr 'Michelson.Typed.T.TUnit)
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary Tezos.Core.Mutez
- Michelson.Test.Gen: instance Test.QuickCheck.Arbitrary.Arbitrary Tezos.Core.Timestamp
- Michelson.Test.Gen: maxTimestamp :: Timestamp
- Michelson.Test.Gen: midTimestamp :: Timestamp
- Michelson.Test.Gen: minTimestamp :: Timestamp
- Michelson.Test.Import: ICEParse :: ParserException -> ImportContractError
- Michelson.Test.Import: ICETypeCheck :: TCError -> ImportContractError
- Michelson.Test.Import: ICEUnexpectedParamType :: ParameterType -> Type -> ImportContractError
- Michelson.Test.Import: ICEUnexpectedStorageType :: Type -> Type -> ImportContractError
- Michelson.Test.Import: concatTestTrees :: [IO [TestTree]] -> IO [TestTree]
- Michelson.Test.Import: data ImportContractError
- Michelson.Test.Import: importContract :: forall cp st. Each '[KnownT] [cp, st] => FilePath -> IO (Contract, Contract cp st)
- Michelson.Test.Import: importUntypedContract :: FilePath -> IO Contract
- Michelson.Test.Import: instance Formatting.Buildable.Buildable Michelson.Test.Import.ImportContractError
- Michelson.Test.Import: instance GHC.Classes.Eq Michelson.Test.Import.ImportContractError
- Michelson.Test.Import: instance GHC.Exception.Type.Exception Michelson.Test.Import.ImportContractError
- Michelson.Test.Import: instance GHC.Show.Show Michelson.Test.Import.ImportContractError
- Michelson.Test.Import: readContract :: forall cp st. Each '[KnownT] [cp, st] => FilePath -> Text -> Either ImportContractError (Contract, Contract cp st)
- Michelson.Test.Import: specWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> Spec) -> Spec
- Michelson.Test.Import: specWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> Spec) -> Spec
- Michelson.Test.Import: specWithUntypedContract :: FilePath -> (Contract -> Spec) -> Spec
- Michelson.Test.Import: testTreesWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test.Import: testTreesWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test.Import: testTreesWithUntypedContract :: HasCallStack => FilePath -> (Contract -> IO [TestTree]) -> IO [TestTree]
- Michelson.Test.Integrational: (?-) :: Text -> a -> (Text, a)
- Michelson.Test.Integrational: CustomTestError :: Text -> TestError
- Michelson.Test.Integrational: ExpectingInterpreterToFail :: TestError
- Michelson.Test.Integrational: IncorrectStorageUpdate :: AddressName -> Text -> TestError
- Michelson.Test.Integrational: IncorrectUpdates :: TestError -> [GStateUpdate] -> TestError
- Michelson.Test.Integrational: InterpreterError :: IntegrationalExecutorError -> TestError
- Michelson.Test.Integrational: InvalidBalance :: AddressName -> ExpectedBalance -> Text -> TestError
- Michelson.Test.Integrational: InvalidStorage :: AddressName -> ExpectedStorage -> Text -> TestError
- Michelson.Test.Integrational: StoragePredicateMismatch :: AddressName -> Text -> TestError
- Michelson.Test.Integrational: TxData :: Address -> Value -> EpName -> Mutez -> TxData
- Michelson.Test.Integrational: UnexpectedInterpreterError :: Text -> IntegrationalExecutorError -> TestError
- Michelson.Test.Integrational: UnexpectedTypeCheckError :: TCError -> TestError
- Michelson.Test.Integrational: UnexpectedUpdates :: NonEmpty GStateUpdate -> TestError
- Michelson.Test.Integrational: ValidatingEmptyScenario :: TestError
- Michelson.Test.Integrational: [tdAmount] :: TxData -> Mutez
- Michelson.Test.Integrational: [tdEntrypoint] :: TxData -> EpName
- Michelson.Test.Integrational: [tdParameter] :: TxData -> Value
- Michelson.Test.Integrational: [tdSenderAddress] :: TxData -> Address
- Michelson.Test.Integrational: attempt :: IntegrationalScenarioM a -> IntegrationalScenarioM (Either ExecutorError a)
- Michelson.Test.Integrational: branchout :: HasCallStack => [(Text, IntegrationalScenario)] -> IntegrationalScenario
- Michelson.Test.Integrational: catchExpectedError :: IntegrationalScenarioM a -> (ExecutorError -> IntegrationalScenarioM b) -> IntegrationalScenarioM b
- Michelson.Test.Integrational: data TestError
- Michelson.Test.Integrational: data TxData
- Michelson.Test.Integrational: expectBalance :: Address -> Mutez -> IntegrationalScenario
- Michelson.Test.Integrational: expectError :: IntegrationalScenarioM a -> IntegrationalScenarioM ExecutorError
- Michelson.Test.Integrational: expectGasExhaustion :: ExecutorError -> IntegrationalScenario
- Michelson.Test.Integrational: expectMichelsonFailed :: (MichelsonFailed -> Bool) -> Address -> ExecutorError -> IntegrationalScenario
- Michelson.Test.Integrational: expectNoStorageUpdates :: IntegrationalScenario
- Michelson.Test.Integrational: expectNoUpdates :: IntegrationalScenario
- Michelson.Test.Integrational: expectStorage :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- Michelson.Test.Integrational: expectStorageConst :: Address -> Value -> IntegrationalScenario
- Michelson.Test.Integrational: expectStorageUpdate :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- Michelson.Test.Integrational: expectStorageUpdateConst :: Address -> Value -> IntegrationalScenario
- Michelson.Test.Integrational: genesisAddress :: Address
- Michelson.Test.Integrational: genesisAddress1 :: Address
- Michelson.Test.Integrational: genesisAddress2 :: Address
- Michelson.Test.Integrational: genesisAddress3 :: Address
- Michelson.Test.Integrational: genesisAddress4 :: Address
- Michelson.Test.Integrational: genesisAddress5 :: Address
- Michelson.Test.Integrational: genesisAddress6 :: Address
- Michelson.Test.Integrational: infixr 0 ?-
- Michelson.Test.Integrational: instance Formatting.Buildable.Buildable Michelson.Test.Integrational.AddressName
- Michelson.Test.Integrational: instance Formatting.Buildable.Buildable Michelson.Test.Integrational.ScenarioBranchName
- Michelson.Test.Integrational: instance Formatting.Buildable.Buildable Michelson.Test.Integrational.ScenarioError
- Michelson.Test.Integrational: instance Formatting.Buildable.Buildable Michelson.Test.Integrational.TestError
- Michelson.Test.Integrational: instance GHC.Exception.Type.Exception Michelson.Test.Integrational.TestError
- Michelson.Test.Integrational: instance GHC.Show.Show Michelson.Test.Integrational.AddressName
- Michelson.Test.Integrational: instance GHC.Show.Show Michelson.Test.Integrational.ExpectedBalance
- Michelson.Test.Integrational: instance GHC.Show.Show Michelson.Test.Integrational.ExpectedStorage
- Michelson.Test.Integrational: instance GHC.Show.Show Michelson.Test.Integrational.TestError
- Michelson.Test.Integrational: integrationalFail :: TestError -> IntegrationalScenarioM anything
- Michelson.Test.Integrational: integrationalTestExpectation :: HasCallStack => IntegrationalScenario -> Expectation
- Michelson.Test.Integrational: integrationalTestProp :: MonadTest m => IntegrationalScenario -> m ()
- Michelson.Test.Integrational: integrationalTestProperty :: IntegrationalScenario -> Property
- Michelson.Test.Integrational: isGState :: Lens' InternalState GState
- Michelson.Test.Integrational: modifyNow :: (Timestamp -> Timestamp) -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: offshoot :: Text -> IntegrationalScenario -> IntegrationalScenario
- Michelson.Test.Integrational: originate :: Contract -> Text -> Value -> Mutez -> IntegrationalScenarioM Address
- Michelson.Test.Integrational: rewindTime :: Integer -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: setChainId :: ChainId -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: setMaxSteps :: RemainingSteps -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: setNow :: Timestamp -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: tExpectStorageConst :: forall st. StorageScope st => Address -> Value st -> IntegrationalScenario
- Michelson.Test.Integrational: tOriginate :: (ParameterScope cp, StorageScope st) => Contract cp st -> Text -> Value st -> Mutez -> IntegrationalScenarioM Address
- Michelson.Test.Integrational: tTransfer :: forall arg. ParameterScope arg => ("from" :! Address) -> ("to" :! Address) -> Mutez -> EpName -> Value arg -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: transfer :: TxData -> Address -> IntegrationalScenarioM ()
- Michelson.Test.Integrational: type IntegrationalScenario = IntegrationalScenarioM ()
- Michelson.Test.Integrational: type IntegrationalScenarioM = StateT InternalState (Except ScenarioError)
- Michelson.Test.Integrational: unexpectedInterpreterError :: ExecutorError -> Text -> IntegrationalScenarioM a
- Michelson.Test.Integrational: withSender :: Address -> IntegrationalScenarioM a -> IntegrationalScenarioM a
- Michelson.Test.Unit: contractHasEntryPoints :: Contract -> Map EpName Type -> Bool
- Michelson.Test.Unit: contractProp :: (IsoValue param, IsoValue storage, ToT param ~ cp, ToT storage ~ st, ParameterScope cp) => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> param -> storage -> prop
- Michelson.Test.Unit: contractPropVal :: ParameterScope cp => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> Value cp -> Value st -> prop
- Michelson.Test.Unit: hasEp :: T -> (EpName, Type) -> Bool
- Michelson.Test.Unit: matchContractEntryPoints :: HasCallStack => Contract -> Map EpName Type -> Either (NonEmpty (EpName, Type)) ()
- Michelson.Test.Unit: mkEntrypointsMap :: Type -> Map EpName Type
- Michelson.Test.Unit: type ContractPropValidator st prop = ContractReturn st -> prop
- Michelson.Test.Unit: type ContractReturn st = (Either MichelsonFailed ([Operation], Value st), InterpreterState)
- Michelson.Test.Unit: validateMichelsonFailsWith :: IsoValue v => v -> ContractPropValidator st Expectation
- Michelson.Test.Unit: validateStorageIs :: IsoValue st => st -> ContractPropValidator (ToT st) Assertion
- Michelson.Test.Unit: validateSuccess :: HasCallStack => ContractPropValidator st Expectation
- Michelson.Test.Util: eitherIsLeft :: (Show b, MonadTest m, HasCallStack) => Either a b -> m ()
- Michelson.Test.Util: eitherIsRight :: (Show a, MonadTest m, HasCallStack) => Either a b -> m ()
- Michelson.Test.Util: failedProp :: Text -> Property
- Michelson.Test.Util: failedTest :: (HasCallStack, MonadTest m) => Text -> m ()
- Michelson.Test.Util: genEither :: MonadGen m => m a -> m b -> m (Either a b)
- Michelson.Test.Util: genTuple2 :: MonadGen m => m a -> m b -> m (a, b)
- Michelson.Test.Util: leftToPrettyPanic :: (Buildable e, HasCallStack) => Either e a -> a
- Michelson.Test.Util: leftToShowPanic :: (Show e, HasCallStack) => Either e a -> a
- Michelson.Test.Util: mcs :: RatioNat -> Time Microsecond
- Michelson.Test.Util: meanTimeUpperBoundProp :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack) => Time unit -> (a -> b) -> a -> Property
- Michelson.Test.Util: meanTimeUpperBoundPropNF :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack, NFData b) => Time unit -> (a -> b) -> a -> Property
- Michelson.Test.Util: minute :: RatioNat -> Time Minute
- Michelson.Test.Util: ms :: RatioNat -> Time Millisecond
- Michelson.Test.Util: qcIsLeft :: Show b => Either a b -> Property
- Michelson.Test.Util: qcIsRight :: Show a => Either a b -> Property
- Michelson.Test.Util: roundtripTest :: forall x y err. (Show x, Show err, Typeable x, Arbitrary x, Eq x, Eq err) => (x -> y) -> (y -> Either err x) -> TestTree
- Michelson.Test.Util: roundtripTree :: forall x y err. (Show x, Show y, Show err, Typeable x, Eq x, Eq err) => Gen x -> (x -> y) -> (y -> Either err x) -> TestTree
- Michelson.Test.Util: runGen :: HasCallStack => Size -> Word64 -> Gen a -> a
- Michelson.Test.Util: sec :: RatioNat -> Time Second
- Michelson.Test.Util: succeededProp :: Property
- Michelson.Test.Util: succeededTest :: MonadTest m => m ()
- Michelson.Test.Util: total :: (MonadTest m, NFData a, HasCallStack) => a -> m a
- Michelson.Text: genMText :: MonadGen m => m MText
- Michelson.TypeCheck: typeCheckTopLevelType :: HasCallStack => TcOriginatedContracts -> Type -> Value -> Either TCError SomeValue
- Michelson.TypeCheck: typeVerifyTopLevelType :: (SingI t, HasCallStack) => TcOriginatedContracts -> Value -> Either TCError (Value t)
- Michelson.TypeCheck.Error: EntryPointNotFound :: EpName -> TCTypeError
- Michelson.TypeCheck.Error: IllegalEntryPoint :: EpNameFromRefAnnError -> TCTypeError
- Michelson.TypeCheck.Instr: typeCheckTopLevelType :: HasCallStack => TcOriginatedContracts -> Type -> Value -> Either TCError SomeValue
- Michelson.TypeCheck.Instr: typeVerifyTopLevelType :: (SingI t, HasCallStack) => TcOriginatedContracts -> Value -> Either TCError (Value t)
- Michelson.TypeCheck.TypeCheck: [tcContractParam] :: TypeCheckEnv -> ~ParameterType
- Michelson.TypeCheck.TypeCheck: [tcContracts] :: TypeCheckEnv -> ~TcOriginatedContracts
- Michelson.TypeCheck.TypeCheck: tcContractParamL :: Lens' TypeCheckEnv ParameterType
- Michelson.TypeCheck.TypeCheck: tcContractsL :: Lens' TypeCheckEnv TcOriginatedContracts
- Michelson.Typed.EntryPoints: AcLeft :: ArmCoord
- Michelson.Typed.EntryPoints: AcRight :: ArmCoord
- Michelson.Typed.EntryPoints: EntryPointCall :: EpName -> Proxy param -> EpLiftSequence arg param -> EntryPointCallT (param :: T) (arg :: T)
- Michelson.Typed.EntryPoints: EpAddress :: Address -> EpName -> EpAddress
- Michelson.Typed.EntryPoints: EpNameUnsafe :: Text -> EpName
- Michelson.Typed.EntryPoints: InEpNameBadAnnotation :: FieldAnn -> EpNameFromRefAnnError
- Michelson.Typed.EntryPoints: ParamEpDuplicatedNames :: NonEmpty EpName -> ParamEpError
- Michelson.Typed.EntryPoints: ParamEpUncallableArm :: ArmCoords -> ParamEpError
- Michelson.Typed.EntryPoints: ParamNotesUnsafe :: Notes t -> RootAnn -> ParamNotes (t :: T)
- Michelson.Typed.EntryPoints: ParseEpAddressBadAddress :: ParseAddressError -> ParseEpAddressError
- Michelson.Typed.EntryPoints: ParseEpAddressBadRefAnn :: Text -> ParseEpAddressError
- Michelson.Typed.EntryPoints: ParseEpAddressRefAnnError :: EpNameFromRefAnnError -> ParseEpAddressError
- Michelson.Typed.EntryPoints: SomeEpc :: EntryPointCallT param arg -> SomeEntryPointCallT (arg :: T)
- Michelson.Typed.EntryPoints: [EplArgHere] :: EpLiftSequence arg arg
- Michelson.Typed.EntryPoints: [EplWrapLeft] :: (KnownT subparam, KnownT r) => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr subparam r)
- Michelson.Typed.EntryPoints: [EplWrapRight] :: (KnownT l, KnownT subparam) => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr l subparam)
- Michelson.Typed.EntryPoints: [MkEntryPointCallRes] :: ParameterScope arg => Notes arg -> EntryPointCallT param arg -> MkEntryPointCallRes param
- Michelson.Typed.EntryPoints: [eaAddress] :: EpAddress -> Address
- Michelson.Typed.EntryPoints: [eaEntryPoint] :: EpAddress -> EpName
- Michelson.Typed.EntryPoints: [epcLiftSequence] :: EntryPointCallT (param :: T) (arg :: T) -> EpLiftSequence arg param
- Michelson.Typed.EntryPoints: [epcName] :: EntryPointCallT (param :: T) (arg :: T) -> EpName
- Michelson.Typed.EntryPoints: [epcParamProxy] :: EntryPointCallT (param :: T) (arg :: T) -> Proxy param
- Michelson.Typed.EntryPoints: [pnNotes] :: ParamNotes (t :: T) -> Notes t
- Michelson.Typed.EntryPoints: [pnRootAnn] :: ParamNotes (t :: T) -> RootAnn
- Michelson.Typed.EntryPoints: [unEpName] :: EpName -> Text
- Michelson.Typed.EntryPoints: data ArmCoord
- Michelson.Typed.EntryPoints: data EntryPointCallT (param :: T) (arg :: T)
- Michelson.Typed.EntryPoints: data EpAddress
- Michelson.Typed.EntryPoints: data EpLiftSequence (arg :: T) (param :: T)
- Michelson.Typed.EntryPoints: data EpNameFromRefAnnError
- Michelson.Typed.EntryPoints: data MkEntryPointCallRes param
- Michelson.Typed.EntryPoints: data ParamEpError
- Michelson.Typed.EntryPoints: data ParamNotes (t :: T)
- Michelson.Typed.EntryPoints: data ParseEpAddressError
- Michelson.Typed.EntryPoints: data SomeEntryPointCallT (arg :: T)
- Michelson.Typed.EntryPoints: epNameFromParamAnn :: FieldAnn -> Maybe EpName
- Michelson.Typed.EntryPoints: epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName
- Michelson.Typed.EntryPoints: epNameToParamAnn :: EpName -> FieldAnn
- Michelson.Typed.EntryPoints: epNameToRefAnn :: EpName -> FieldAnn
- Michelson.Typed.EntryPoints: epcCallRootUnsafe :: ParameterScope param => EntryPointCallT param param
- Michelson.Typed.EntryPoints: epcPrimitive :: forall p. (ParameterScope p, ForbidOr p) => EntryPointCallT p p
- Michelson.Typed.EntryPoints: flattenEntryPoints :: SingI t => ParamNotes t -> Map EpName Type
- Michelson.Typed.EntryPoints: formatEpAddress :: EpAddress -> Text
- Michelson.Typed.EntryPoints: genEpAddress :: (MonadGen m, GenBase m ~ Identity) => m EpAddress
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData (Michelson.Typed.EntryPoints.EntryPointCallT param arg)
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData (Michelson.Typed.EntryPoints.EpLiftSequence param arg)
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData (Michelson.Typed.EntryPoints.ParamNotes t)
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData (Michelson.Typed.EntryPoints.SomeEntryPointCallT arg)
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData Michelson.Typed.EntryPoints.ArmCoord
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData Michelson.Typed.EntryPoints.ParamEpError
- Michelson.Typed.EntryPoints: instance Control.DeepSeq.NFData Michelson.Typed.EntryPoints.ParseEpAddressError
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable (Michelson.Typed.EntryPoints.EntryPointCallT param arg)
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable (Michelson.Typed.EntryPoints.EpLiftSequence arg param)
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable (Michelson.Typed.EntryPoints.SomeEntryPointCallT arg)
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Typed.EntryPoints.ArmCoord
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Typed.EntryPoints.ParamEpError
- Michelson.Typed.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Typed.EntryPoints.ParseEpAddressError
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq (Michelson.Typed.EntryPoints.EntryPointCallT param arg)
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq (Michelson.Typed.EntryPoints.EpLiftSequence arg param)
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq (Michelson.Typed.EntryPoints.ParamNotes t)
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq (Michelson.Typed.EntryPoints.SomeEntryPointCallT arg)
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq Michelson.Typed.EntryPoints.ArmCoord
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq Michelson.Typed.EntryPoints.ParamEpError
- Michelson.Typed.EntryPoints: instance GHC.Classes.Eq Michelson.Typed.EntryPoints.ParseEpAddressError
- Michelson.Typed.EntryPoints: instance GHC.Classes.Ord Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance GHC.Generics.Generic (Michelson.Typed.EntryPoints.ParamNotes t)
- Michelson.Typed.EntryPoints: instance GHC.Generics.Generic Michelson.Typed.EntryPoints.ArmCoord
- Michelson.Typed.EntryPoints: instance GHC.Generics.Generic Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance GHC.Generics.Generic Michelson.Typed.EntryPoints.ParamEpError
- Michelson.Typed.EntryPoints: instance GHC.Generics.Generic Michelson.Typed.EntryPoints.ParseEpAddressError
- Michelson.Typed.EntryPoints: instance GHC.Show.Show (Michelson.Typed.EntryPoints.EntryPointCallT param arg)
- Michelson.Typed.EntryPoints: instance GHC.Show.Show (Michelson.Typed.EntryPoints.EpLiftSequence arg param)
- Michelson.Typed.EntryPoints: instance GHC.Show.Show (Michelson.Typed.EntryPoints.ParamNotes t)
- Michelson.Typed.EntryPoints: instance GHC.Show.Show (Michelson.Typed.EntryPoints.SomeEntryPointCallT arg)
- Michelson.Typed.EntryPoints: instance GHC.Show.Show Michelson.Typed.EntryPoints.ArmCoord
- Michelson.Typed.EntryPoints: instance GHC.Show.Show Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: instance GHC.Show.Show Michelson.Typed.EntryPoints.ParamEpError
- Michelson.Typed.EntryPoints: instance GHC.Show.Show Michelson.Typed.EntryPoints.ParseEpAddressError
- Michelson.Typed.EntryPoints: instance Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Annotation.FieldAnn => Test.QuickCheck.Arbitrary.Arbitrary Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.EntryPoints: mformatEpAddress :: EpAddress -> MText
- Michelson.Typed.EntryPoints: mkEntryPointCall :: ParameterScope param => EpName -> ParamNotes param -> Maybe (MkEntryPointCallRes param)
- Michelson.Typed.EntryPoints: mkParamNotes :: Notes t -> RootAnn -> Either ParamEpError (ParamNotes t)
- Michelson.Typed.EntryPoints: newtype EpName
- Michelson.Typed.EntryPoints: parseEpAddress :: Text -> Either ParseEpAddressError EpAddress
- Michelson.Typed.EntryPoints: pattern DefEpName :: EpName
- Michelson.Typed.EntryPoints: pattern ParamNotes :: Notes t -> RootAnn -> ParamNotes t
- Michelson.Typed.EntryPoints: sepcCallRootUnsafe :: ParameterScope param => SomeEntryPointCallT param
- Michelson.Typed.EntryPoints: sepcName :: SomeEntryPointCallT arg -> EpName
- Michelson.Typed.EntryPoints: sepcPrimitive :: forall t. (ParameterScope t, ForbidOr t) => SomeEntryPointCallT t
- Michelson.Typed.EntryPoints: starParamNotes :: SingI t => ParamNotes t
- Michelson.Typed.EntryPoints: tyImplicitAccountParam :: ParamNotes 'TUnit
- Michelson.Typed.EntryPoints: type ArmCoords = [ArmCoord]
- Michelson.Typed.EntryPoints: type family ForbidOr (t :: T) :: Constraint
- Michelson.Typed.EntryPoints: unsafeParseEpAddress :: HasCallStack => Text -> EpAddress
- Michelson.Typed.Haskell: [crEntryPoint] :: ContractRef (arg :: Type) -> SomeEntryPointCall arg
- Michelson.Typed.Haskell: genBigMap :: forall k v m. (MonadGen m, Ord k, WellTypedToT k, WellTypedToT v, Comparable (ToT k)) => m k -> m v -> m (BigMap k v)
- Michelson.Typed.Haskell: type EntryPointCall param arg = EntryPointCallT (ToT param) (ToT arg)
- Michelson.Typed.Haskell: type SomeEntryPointCall arg = SomeEntryPointCallT (ToT arg)
- Michelson.Typed.Haskell.Doc: instance Michelson.Typed.Haskell.Doc.GTypeHasDoc x => Michelson.Typed.Haskell.Doc.GTypeHasDoc (GHC.Generics.D1 i x)
- Michelson.Typed.Haskell.Doc: instance Michelson.Typed.Haskell.Doc.TypeHasDoc Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.Haskell.Value: [crEntryPoint] :: ContractRef (arg :: Type) -> SomeEntryPointCall arg
- Michelson.Typed.Haskell.Value: genBigMap :: forall k v m. (MonadGen m, Ord k, WellTypedToT k, WellTypedToT v, Comparable (ToT k)) => m k -> m v -> m (BigMap k v)
- Michelson.Typed.Haskell.Value: instance Michelson.Typed.Haskell.Value.IsoValue Michelson.Typed.EntryPoints.EpAddress
- Michelson.Typed.Haskell.Value: type EntryPointCall param arg = EntryPointCallT (ToT param) (ToT arg)
- Michelson.Typed.Haskell.Value: type SomeEntryPointCall arg = SomeEntryPointCallT (ToT arg)
- Michelson.Untyped.EntryPoints: EpNameUnsafe :: Text -> EpName
- Michelson.Untyped.EntryPoints: InEpNameBadAnnotation :: FieldAnn -> EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: [unEpName] :: EpName -> Text
- Michelson.Untyped.EntryPoints: buildEpName :: Text -> Either String EpName
- Michelson.Untyped.EntryPoints: data EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: epNameFromParamAnn :: FieldAnn -> Maybe EpName
- Michelson.Untyped.EntryPoints: epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName
- Michelson.Untyped.EntryPoints: epNameToParamAnn :: EpName -> FieldAnn
- Michelson.Untyped.EntryPoints: epNameToRefAnn :: EpName -> FieldAnn
- Michelson.Untyped.EntryPoints: instance Control.DeepSeq.NFData Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Control.DeepSeq.NFData Michelson.Untyped.EntryPoints.EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: instance Data.Aeson.Types.FromJSON.FromJSON Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Data.Aeson.Types.ToJSON.ToJSON Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Data.Default.Class.Default Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Formatting.Buildable.Buildable Michelson.Untyped.EntryPoints.EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: instance GHC.Classes.Eq Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance GHC.Classes.Eq Michelson.Untyped.EntryPoints.EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: instance GHC.Classes.Ord Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance GHC.Generics.Generic Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance GHC.Generics.Generic Michelson.Untyped.EntryPoints.EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: instance GHC.Show.Show Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance GHC.Show.Show Michelson.Untyped.EntryPoints.EpNameFromRefAnnError
- Michelson.Untyped.EntryPoints: instance Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Annotation.FieldAnn => Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: instance Util.CLI.HasCLReader Michelson.Untyped.EntryPoints.EpName
- Michelson.Untyped.EntryPoints: mkEntrypointsMap :: Type -> Map EpName Type
- Michelson.Untyped.EntryPoints: newtype EpName
- Michelson.Untyped.EntryPoints: pattern DefEpName :: EpName
- Michelson.Untyped.EntryPoints: unsafeBuildEpName :: Text -> EpName
- Morley.Micheline.Expression: genExprAnnotation :: forall m. (MonadGen m, GenBase m ~ Identity) => m Annotation
- Morley.Micheline.Expression: genExpression :: forall m. (MonadGen m, GenBase m ~ Identity) => m Expression
- Morley.Micheline.Expression: genExpressionSeq :: forall m. (MonadGen m, GenBase m ~ Identity) => m (Seq Expression)
- Morley.Micheline.Expression: genMichelinePrimAp :: forall m. (MonadGen m, GenBase m ~ Identity) => m MichelinePrimAp
- Tezos.Address: genAddress :: MonadGen m => m Address
- Tezos.Address: instance Data.Aeson.Types.FromJSON.FromJSON Tezos.Address.ContractHash
- Tezos.Address: instance Data.Aeson.Types.FromJSON.FromJSONKey Tezos.Address.ContractHash
- Tezos.Address: instance Data.Aeson.Types.ToJSON.ToJSON Tezos.Address.ContractHash
- Tezos.Address: instance Data.Aeson.Types.ToJSON.ToJSONKey Tezos.Address.ContractHash
- Tezos.Core: genChainId :: MonadGen m => m ChainId
- Tezos.Crypto: genKeyHash :: MonadGen m => m KeyHash
- Tezos.Crypto: genKeyHashTag :: MonadGen m => m KeyHashTag
- Tezos.Crypto: genPublicKey :: MonadGen m => m PublicKey
- Tezos.Crypto: genSecretKey :: MonadGen m => m SecretKey
- Tezos.Crypto: genSignature :: MonadGen m => m Signature
- Tezos.Crypto.Ed25519: genPublicKey :: MonadGen m => m PublicKey
- Tezos.Crypto.Ed25519: genSecretKey :: MonadGen m => m SecretKey
- Tezos.Crypto.Ed25519: genSignature :: MonadGen m => m Signature
- Tezos.Crypto.P256: genPublicKey :: MonadGen m => m PublicKey
- Tezos.Crypto.P256: genSecretKey :: MonadGen m => m SecretKey
- Tezos.Crypto.P256: genSignature :: MonadGen m => m Signature
- Tezos.Crypto.Secp256k1: [pkBytes] :: PublicKey -> Maybe ByteString
- Tezos.Crypto.Secp256k1: data PublicKey
- Tezos.Crypto.Secp256k1: genPublicKey :: MonadGen m => m PublicKey
- Tezos.Crypto.Secp256k1: genSecretKey :: MonadGen m => m SecretKey
- Tezos.Crypto.Secp256k1: genSignature :: MonadGen m => m Signature
- Util.Named: instance Control.Lens.Wrapped.Wrapped (Named.Internal.NamedF Data.Functor.Identity.Identity a name)
- Util.Named: instance Control.Lens.Wrapped.Wrapped (Named.Internal.NamedF GHC.Maybe.Maybe a name)
- Util.Test.Gen: genAnnotation :: forall m a. (MonadGen m, GenBase m ~ Identity) => m (Annotation a)
- Util.Test.Gen: genContract :: (MonadGen m, GenBase m ~ Identity) => m Contract
- Util.Test.Gen: genContract' :: (MonadGen m, GenBase m ~ Identity) => m op -> m (Contract' op)
- Util.Test.Gen: genElt :: MonadGen m => m op -> m (Elt op)
- Util.Test.Gen: genEpName :: (MonadGen m, GenBase m ~ Identity) => m EpName
- Util.Test.Gen: genExpandedOp :: (MonadGen m, GenBase m ~ Identity) => m ExpandedOp
- Util.Test.Gen: genExtInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (ExtInstrAbstract op)
- Util.Test.Gen: genInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (InstrAbstract op)
- Util.Test.Gen: genInstrCallStack :: MonadGen m => m InstrCallStack
- Util.Test.Gen: genInternalByteString :: MonadGen m => m InternalByteString
- Util.Test.Gen: genLetName :: MonadGen m => m LetName
- Util.Test.Gen: genParameterType :: (MonadGen m, GenBase m ~ Identity) => m ParameterType
- Util.Test.Gen: genPos :: MonadGen m => m Pos
- Util.Test.Gen: genPrintComment :: MonadGen m => m PrintComment
- Util.Test.Gen: genSrcPos :: MonadGen m => m SrcPos
- Util.Test.Gen: genStackFn :: (MonadGen m, GenBase m ~ Identity) => m StackFn
- Util.Test.Gen: genStackRef :: MonadGen m => m StackRef
- Util.Test.Gen: genStackTypePattern :: (MonadGen m, GenBase m ~ Identity) => m StackTypePattern
- Util.Test.Gen: genT :: (MonadGen m, GenBase m ~ Identity) => m T
- Util.Test.Gen: genTestAssert :: MonadGen m => m op -> m (TestAssert op)
- Util.Test.Gen: genTyVar :: (MonadGen m, GenBase m ~ Identity) => m TyVar
- Util.Test.Gen: genType :: (MonadGen m, GenBase m ~ Identity) => m Type
- Util.Test.Gen: genValue :: (MonadGen m, GenBase m ~ Identity) => m Value
- Util.Test.Gen: genValue' :: MonadGen m => m op -> m (Value' op)
- Util.Test.Gen: genVar :: MonadGen m => m Var
- Util.Test.Ingredients: ourIngredients :: [Ingredient]
+ Michelson.Doc: docItemPos :: DocItem d => Natural
+ Michelson.Macro: UF :: (VarAnn, FieldAnn) -> UnpairStruct
+ Michelson.Macro: UP :: UnpairStruct -> UnpairStruct -> UnpairStruct
+ Michelson.Macro: data UnpairStruct
+ Michelson.Macro: instance Control.DeepSeq.NFData Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance Data.Aeson.Types.FromJSON.FromJSON Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance Data.Aeson.Types.ToJSON.ToJSON Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance Data.Data.Data Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance Formatting.Buildable.Buildable Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance GHC.Classes.Eq Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance GHC.Generics.Generic Michelson.Macro.UnpairStruct
+ Michelson.Macro: instance GHC.Show.Show Michelson.Macro.UnpairStruct
+ Michelson.Macro: mapPairLeaves :: [FieldAnn] -> PairStruct -> PairStruct
+ Michelson.Macro: mapUnpairLeaves :: [(VarAnn, FieldAnn)] -> UnpairStruct -> UnpairStruct
+ Michelson.Parser: ops :: Parser [ParsedOp]
+ Michelson.Runtime.GState: GStateStorageNotMatch :: Address -> GStateUpdateError
+ Michelson.Runtime.GState: [GSAddAddress] :: Address -> AddressState -> GStateUpdate
+ Michelson.Runtime.GState: [GSIncrementCounter] :: GStateUpdate
+ Michelson.Runtime.GState: [GSSetBalance] :: Address -> Mutez -> GStateUpdate
+ Michelson.Runtime.GState: [GSSetStorageValue] :: StorageScope st => Address -> Value st -> GStateUpdate
+ Michelson.Runtime.GState: mkContractState :: (TCError -> err) -> (TCError -> err) -> (Mutez, Contract, Value) -> Either err ContractState
+ Michelson.Text: maxBoundMChar :: Int
+ Michelson.Text: minBoundMChar :: Int
+ Michelson.TypeCheck: typeCheckParameter :: TcOriginatedContracts -> Type -> Value -> Either TCError SomeValue
+ Michelson.TypeCheck: typeCheckStorage :: Type -> Value -> Either TCError SomeValue
+ Michelson.TypeCheck: typeVerifyParameter :: SingI t => TcOriginatedContracts -> Value -> Either TCError (Value t)
+ Michelson.TypeCheck: typeVerifyStorage :: SingI t => Value -> Either TCError (Value t)
+ Michelson.TypeCheck.Error: EntrypointNotFound :: EpName -> TCTypeError
+ Michelson.TypeCheck.Error: IllegalEntrypoint :: EpNameFromRefAnnError -> TCTypeError
+ Michelson.TypeCheck.Instr: typeCheckParameter :: TcOriginatedContracts -> Type -> Value -> Either TCError SomeValue
+ Michelson.TypeCheck.Instr: typeCheckStorage :: Type -> Value -> Either TCError SomeValue
+ Michelson.TypeCheck.Instr: typeVerifyParameter :: SingI t => TcOriginatedContracts -> Value -> Either TCError (Value t)
+ Michelson.TypeCheck.Instr: typeVerifyStorage :: SingI t => Value -> Either TCError (Value t)
+ Michelson.TypeCheck.TypeCheck: SomeParamType :: Sing t -> ParamNotes t -> SomeParamType
+ Michelson.TypeCheck.TypeCheck: TypeCheckContract :: SomeParamType -> TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: TypeCheckPack :: TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: TypeCheckTest :: TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: TypeCheckValue :: (Value, T) -> TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: [tcMode] :: TypeCheckEnv -> ~TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: data SomeParamType
+ Michelson.TypeCheck.TypeCheck: data TypeCheckMode
+ Michelson.TypeCheck.TypeCheck: instance Formatting.Buildable.Buildable Michelson.TypeCheck.TypeCheck.SomeParamType
+ Michelson.TypeCheck.TypeCheck: instance GHC.Classes.Eq Michelson.TypeCheck.TypeCheck.SomeParamType
+ Michelson.TypeCheck.TypeCheck: instance GHC.Show.Show Michelson.TypeCheck.TypeCheck.SomeParamType
+ Michelson.TypeCheck.TypeCheck: mkSomeParamType :: ParameterType -> Either TCError SomeParamType
+ Michelson.TypeCheck.TypeCheck: mkSomeParamTypeUnsafe :: HasCallStack => ParameterType -> SomeParamType
+ Michelson.TypeCheck.TypeCheck: tcModeL :: Lens' TypeCheckEnv TypeCheckMode
+ Michelson.TypeCheck.Types: NotWellTyped :: T -> NotWellTyped
+ Michelson.TypeCheck.Types: data NotWellTyped
+ Michelson.TypeCheck.Types: unsafeWithWTP :: forall t a. SingI t => (WellTyped t => a) -> a
+ Michelson.TypeCheck.Value: tcFailedOnValue :: Value -> T -> Text -> Maybe TCTypeError -> TypeCheckInstr a
+ Michelson.Typed.Arith: convergeArith :: ArithOp aop n m => proxy aop -> Notes n -> Notes m -> Either AnnConvergeError (Notes (ArithRes aop n m))
+ Michelson.Typed.Entrypoints: AcLeft :: ArmCoord
+ Michelson.Typed.Entrypoints: AcRight :: ArmCoord
+ Michelson.Typed.Entrypoints: EntrypointCall :: EpName -> Proxy param -> EpLiftSequence arg param -> EntrypointCallT (param :: T) (arg :: T)
+ Michelson.Typed.Entrypoints: EpAddress :: Address -> EpName -> EpAddress
+ Michelson.Typed.Entrypoints: EpNameUnsafe :: Text -> EpName
+ Michelson.Typed.Entrypoints: InEpNameBadAnnotation :: FieldAnn -> EpNameFromRefAnnError
+ Michelson.Typed.Entrypoints: ParamEpDuplicatedNames :: NonEmpty EpName -> ParamEpError
+ Michelson.Typed.Entrypoints: ParamEpUncallableArm :: ArmCoords -> ParamEpError
+ Michelson.Typed.Entrypoints: ParamNotesUnsafe :: Notes t -> RootAnn -> ParamNotes (t :: T)
+ Michelson.Typed.Entrypoints: ParseEpAddressBadAddress :: ParseAddressError -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: ParseEpAddressBadEntryopint :: ByteString -> UnicodeException -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: ParseEpAddressBadRefAnn :: Text -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: ParseEpAddressInvalidLength :: Int -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: ParseEpAddressRawBadAddress :: ParseAddressRawError -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: ParseEpAddressRefAnnError :: EpNameFromRefAnnError -> ParseEpAddressError
+ Michelson.Typed.Entrypoints: SomeEpc :: EntrypointCallT param arg -> SomeEntrypointCallT (arg :: T)
+ Michelson.Typed.Entrypoints: [EplArgHere] :: EpLiftSequence arg arg
+ Michelson.Typed.Entrypoints: [EplWrapLeft] :: (KnownT subparam, KnownT r) => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr subparam r)
+ Michelson.Typed.Entrypoints: [EplWrapRight] :: (KnownT l, KnownT subparam) => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr l subparam)
+ Michelson.Typed.Entrypoints: [MkEntrypointCallRes] :: ParameterScope arg => Notes arg -> EntrypointCallT param arg -> MkEntrypointCallRes param
+ Michelson.Typed.Entrypoints: [eaAddress] :: EpAddress -> Address
+ Michelson.Typed.Entrypoints: [eaEntrypoint] :: EpAddress -> EpName
+ Michelson.Typed.Entrypoints: [epcLiftSequence] :: EntrypointCallT (param :: T) (arg :: T) -> EpLiftSequence arg param
+ Michelson.Typed.Entrypoints: [epcName] :: EntrypointCallT (param :: T) (arg :: T) -> EpName
+ Michelson.Typed.Entrypoints: [epcParamProxy] :: EntrypointCallT (param :: T) (arg :: T) -> Proxy param
+ Michelson.Typed.Entrypoints: [pnNotes] :: ParamNotes (t :: T) -> Notes t
+ Michelson.Typed.Entrypoints: [pnRootAnn] :: ParamNotes (t :: T) -> RootAnn
+ Michelson.Typed.Entrypoints: [unEpName] :: EpName -> Text
+ Michelson.Typed.Entrypoints: data ArmCoord
+ Michelson.Typed.Entrypoints: data EntrypointCallT (param :: T) (arg :: T)
+ Michelson.Typed.Entrypoints: data EpAddress
+ Michelson.Typed.Entrypoints: data EpLiftSequence (arg :: T) (param :: T)
+ Michelson.Typed.Entrypoints: data EpNameFromRefAnnError
+ Michelson.Typed.Entrypoints: data MkEntrypointCallRes param
+ Michelson.Typed.Entrypoints: data ParamEpError
+ Michelson.Typed.Entrypoints: data ParamNotes (t :: T)
+ Michelson.Typed.Entrypoints: data ParseEpAddressError
+ Michelson.Typed.Entrypoints: data SomeEntrypointCallT (arg :: T)
+ Michelson.Typed.Entrypoints: epNameFromParamAnn :: FieldAnn -> Maybe EpName
+ Michelson.Typed.Entrypoints: epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName
+ Michelson.Typed.Entrypoints: epNameToParamAnn :: EpName -> FieldAnn
+ Michelson.Typed.Entrypoints: epNameToRefAnn :: EpName -> FieldAnn
+ Michelson.Typed.Entrypoints: epcCallRootUnsafe :: ParameterScope param => EntrypointCallT param param
+ Michelson.Typed.Entrypoints: epcPrimitive :: forall p. (ParameterScope p, ForbidOr p) => EntrypointCallT p p
+ Michelson.Typed.Entrypoints: flattenEntrypoints :: SingI t => ParamNotes t -> Map EpName Type
+ Michelson.Typed.Entrypoints: formatEpAddress :: EpAddress -> Text
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData (Michelson.Typed.Entrypoints.EntrypointCallT param arg)
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData (Michelson.Typed.Entrypoints.EpLiftSequence param arg)
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData (Michelson.Typed.Entrypoints.ParamNotes t)
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData (Michelson.Typed.Entrypoints.SomeEntrypointCallT arg)
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData Michelson.Typed.Entrypoints.ArmCoord
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData Michelson.Typed.Entrypoints.ParamEpError
+ Michelson.Typed.Entrypoints: instance Control.DeepSeq.NFData Michelson.Typed.Entrypoints.ParseEpAddressError
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable (Michelson.Typed.Entrypoints.EntrypointCallT param arg)
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable (Michelson.Typed.Entrypoints.EpLiftSequence arg param)
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable (Michelson.Typed.Entrypoints.SomeEntrypointCallT arg)
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Typed.Entrypoints.ArmCoord
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Typed.Entrypoints.ParamEpError
+ Michelson.Typed.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Typed.Entrypoints.ParseEpAddressError
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq (Michelson.Typed.Entrypoints.EntrypointCallT param arg)
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq (Michelson.Typed.Entrypoints.EpLiftSequence arg param)
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq (Michelson.Typed.Entrypoints.ParamNotes t)
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq (Michelson.Typed.Entrypoints.SomeEntrypointCallT arg)
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq Michelson.Typed.Entrypoints.ArmCoord
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq Michelson.Typed.Entrypoints.ParamEpError
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Eq Michelson.Typed.Entrypoints.ParseEpAddressError
+ Michelson.Typed.Entrypoints: instance GHC.Classes.Ord Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance GHC.Generics.Generic (Michelson.Typed.Entrypoints.ParamNotes t)
+ Michelson.Typed.Entrypoints: instance GHC.Generics.Generic Michelson.Typed.Entrypoints.ArmCoord
+ Michelson.Typed.Entrypoints: instance GHC.Generics.Generic Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance GHC.Generics.Generic Michelson.Typed.Entrypoints.ParamEpError
+ Michelson.Typed.Entrypoints: instance GHC.Generics.Generic Michelson.Typed.Entrypoints.ParseEpAddressError
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show (Michelson.Typed.Entrypoints.EntrypointCallT param arg)
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show (Michelson.Typed.Entrypoints.EpLiftSequence arg param)
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show (Michelson.Typed.Entrypoints.ParamNotes t)
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show (Michelson.Typed.Entrypoints.SomeEntrypointCallT arg)
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show Michelson.Typed.Entrypoints.ArmCoord
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show Michelson.Typed.Entrypoints.ParamEpError
+ Michelson.Typed.Entrypoints: instance GHC.Show.Show Michelson.Typed.Entrypoints.ParseEpAddressError
+ Michelson.Typed.Entrypoints: instance Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Annotation.FieldAnn => Test.QuickCheck.Arbitrary.Arbitrary Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Entrypoints: mformatEpAddress :: EpAddress -> MText
+ Michelson.Typed.Entrypoints: mkEntrypointCall :: ParameterScope param => EpName -> ParamNotes param -> Maybe (MkEntrypointCallRes param)
+ Michelson.Typed.Entrypoints: mkParamNotes :: Notes t -> RootAnn -> Either ParamEpError (ParamNotes t)
+ Michelson.Typed.Entrypoints: newtype EpName
+ Michelson.Typed.Entrypoints: parseEpAddress :: Text -> Either ParseEpAddressError EpAddress
+ Michelson.Typed.Entrypoints: parseEpAddressRaw :: ByteString -> Either ParseEpAddressError EpAddress
+ Michelson.Typed.Entrypoints: pattern DefEpName :: EpName
+ Michelson.Typed.Entrypoints: pattern ParamNotes :: Notes t -> RootAnn -> ParamNotes t
+ Michelson.Typed.Entrypoints: sepcCallRootUnsafe :: ParameterScope param => SomeEntrypointCallT param
+ Michelson.Typed.Entrypoints: sepcName :: SomeEntrypointCallT arg -> EpName
+ Michelson.Typed.Entrypoints: sepcPrimitive :: forall t. (ParameterScope t, ForbidOr t) => SomeEntrypointCallT t
+ Michelson.Typed.Entrypoints: starParamNotes :: SingI t => ParamNotes t
+ Michelson.Typed.Entrypoints: tyImplicitAccountParam :: ParamNotes 'TUnit
+ Michelson.Typed.Entrypoints: type ArmCoords = [ArmCoord]
+ Michelson.Typed.Entrypoints: type family ForbidOr (t :: T) :: Constraint
+ Michelson.Typed.Entrypoints: unsafeParseEpAddress :: HasCallStack => Text -> EpAddress
+ Michelson.Typed.Entrypoints: unsafeParseEpAddressRaw :: ByteString -> EpAddress
+ Michelson.Typed.Haskell: [crEntrypoint] :: ContractRef (arg :: Type) -> SomeEntrypointCall arg
+ Michelson.Typed.Haskell: haskellAddNewtypeField :: Text -> TypeDocHaskellRep a -> TypeDocHaskellRep a
+ Michelson.Typed.Haskell: type EntrypointCall param arg = EntrypointCallT (ToT param) (ToT arg)
+ Michelson.Typed.Haskell: type SomeEntrypointCall arg = SomeEntrypointCallT (ToT arg)
+ Michelson.Typed.Haskell.Doc: haskellAddNewtypeField :: Text -> TypeDocHaskellRep a -> TypeDocHaskellRep a
+ Michelson.Typed.Haskell.Doc: instance Michelson.Typed.Haskell.Doc.GTypeHasDoc x => Michelson.Typed.Haskell.Doc.GTypeHasDoc (GHC.Generics.D1 ('GHC.Generics.MetaData _a _b _c 'GHC.Types.False) x)
+ Michelson.Typed.Haskell.Doc: instance Michelson.Typed.Haskell.Doc.GTypeHasDoc x => Michelson.Typed.Haskell.Doc.GTypeHasDoc (GHC.Generics.D1 ('GHC.Generics.MetaData _a _b _c 'GHC.Types.True) x)
+ Michelson.Typed.Haskell.Doc: instance Michelson.Typed.Haskell.Doc.TypeHasDoc Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Haskell.Value: [crEntrypoint] :: ContractRef (arg :: Type) -> SomeEntrypointCall arg
+ Michelson.Typed.Haskell.Value: instance Michelson.Typed.Haskell.Value.IsoValue Michelson.Typed.Entrypoints.EpAddress
+ Michelson.Typed.Haskell.Value: type EntrypointCall param arg = EntrypointCallT (ToT param) (ToT arg)
+ Michelson.Typed.Haskell.Value: type SomeEntrypointCall arg = SomeEntrypointCallT (ToT arg)
+ Michelson.Typed.Instr: [cEntriesOrder] :: Contract cp st -> EntriesOrder
+ Michelson.Typed.Instr: mapEntriesOrdered :: Contract cp st -> (ParamNotes cp -> a) -> (Notes st -> a) -> (ContractCode cp st -> a) -> [a]
+ Michelson.Typed.Polymorphic: convergeEDiv :: EDivOp n m => Notes n -> Notes m -> Either AnnConvergeError (Notes ('TOption ('TPair (EDivOpRes n m) (EModOpRes n m))))
+ Michelson.Typed.Util: ceaBottomToTop :: CtorEffectsApp x
+ Michelson.Untyped.Contract: CBCode :: [op] -> ContractBlock op
+ Michelson.Untyped.Contract: CBParam :: ParameterType -> ContractBlock op
+ Michelson.Untyped.Contract: CBStorage :: Type -> ContractBlock op
+ Michelson.Untyped.Contract: CPS :: EntriesOrder
+ Michelson.Untyped.Contract: CSP :: EntriesOrder
+ Michelson.Untyped.Contract: PCS :: EntriesOrder
+ Michelson.Untyped.Contract: PSC :: EntriesOrder
+ Michelson.Untyped.Contract: SCP :: EntriesOrder
+ Michelson.Untyped.Contract: SPC :: EntriesOrder
+ Michelson.Untyped.Contract: [entriesOrder] :: Contract' op -> EntriesOrder
+ Michelson.Untyped.Contract: canonicalEntriesOrder :: EntriesOrder
+ Michelson.Untyped.Contract: data ContractBlock op
+ Michelson.Untyped.Contract: data EntriesOrder
+ Michelson.Untyped.Contract: entriesOrderToInt :: EntriesOrder -> (Int, Int, Int)
+ Michelson.Untyped.Contract: instance Control.DeepSeq.NFData Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance Data.Aeson.Types.FromJSON.FromJSON Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance Data.Aeson.Types.ToJSON.ToJSON Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance Data.Data.Data Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance Data.Default.Class.Default Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Classes.Eq Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Classes.Eq op => GHC.Classes.Eq (Michelson.Untyped.Contract.ContractBlock op)
+ Michelson.Untyped.Contract: instance GHC.Enum.Bounded Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Enum.Enum Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Generics.Generic Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Show.Show Michelson.Untyped.Contract.EntriesOrder
+ Michelson.Untyped.Contract: instance GHC.Show.Show op => GHC.Show.Show (Michelson.Untyped.Contract.ContractBlock op)
+ Michelson.Untyped.Contract: mapEntriesOrdered :: Contract' op -> (ParameterType -> a) -> (Storage -> a) -> ([op] -> a) -> [a]
+ Michelson.Untyped.Contract: orderContractBlock :: (ContractBlock op, ContractBlock op, ContractBlock op) -> Maybe (Contract' op)
+ Michelson.Untyped.Entrypoints: EpNameUnsafe :: Text -> EpName
+ Michelson.Untyped.Entrypoints: InEpNameBadAnnotation :: FieldAnn -> EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: [unEpName] :: EpName -> Text
+ Michelson.Untyped.Entrypoints: buildEpName :: Text -> Either String EpName
+ Michelson.Untyped.Entrypoints: data EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: epNameFromParamAnn :: FieldAnn -> Maybe EpName
+ Michelson.Untyped.Entrypoints: epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName
+ Michelson.Untyped.Entrypoints: epNameToParamAnn :: EpName -> FieldAnn
+ Michelson.Untyped.Entrypoints: epNameToRefAnn :: EpName -> FieldAnn
+ Michelson.Untyped.Entrypoints: instance Control.DeepSeq.NFData Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Control.DeepSeq.NFData Michelson.Untyped.Entrypoints.EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: instance Data.Aeson.Types.FromJSON.FromJSON Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Data.Aeson.Types.ToJSON.ToJSON Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Data.Default.Class.Default Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Formatting.Buildable.Buildable Michelson.Untyped.Entrypoints.EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: instance GHC.Classes.Eq Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance GHC.Classes.Eq Michelson.Untyped.Entrypoints.EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: instance GHC.Classes.Ord Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance GHC.Generics.Generic Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance GHC.Generics.Generic Michelson.Untyped.Entrypoints.EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: instance GHC.Show.Show Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance GHC.Show.Show Michelson.Untyped.Entrypoints.EpNameFromRefAnnError
+ Michelson.Untyped.Entrypoints: instance Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Annotation.FieldAnn => Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: instance Util.CLI.HasCLReader Michelson.Untyped.Entrypoints.EpName
+ Michelson.Untyped.Entrypoints: mkEntrypointsMap :: Type -> Map EpName Type
+ Michelson.Untyped.Entrypoints: newtype EpName
+ Michelson.Untyped.Entrypoints: pattern DefEpName :: EpName
+ Michelson.Untyped.Entrypoints: unsafeBuildEpName :: Text -> EpName
+ Morley.Micheline.Class: FromExpressionError :: UnpackError -> FromExpressionError
+ Morley.Micheline.Class: data FromExpressionError
+ Morley.Micheline.Class: instance Formatting.Buildable.Buildable Morley.Micheline.Class.FromExpressionError
+ Morley.Micheline.Class: instance GHC.Classes.Eq Morley.Micheline.Class.FromExpressionError
+ Morley.Micheline.Class: instance GHC.Exception.Type.Exception Morley.Micheline.Class.FromExpressionError
+ Morley.Micheline.Class: instance GHC.Show.Show Morley.Micheline.Class.FromExpressionError
+ Morley.Micheline.Class: instance Morley.Micheline.Class.FromExpression Michelson.Untyped.Aliases.Contract
+ Morley.Micheline.Expression: instance Formatting.Buildable.Buildable Morley.Micheline.Expression.Expression
+ Tezos.Address: ParseAddressRawInvalidPrefix :: ByteString -> ParseAddressRawError
+ Tezos.Address: ParseAddressRawMalformedSeparator :: ByteString -> ParseAddressRawError
+ Tezos.Address: ParseAddressRawWrongSize :: ByteString -> ParseAddressRawError
+ Tezos.Address: ParseContractAddressWrongBase58Check :: ParseContractAddressError
+ Tezos.Address: ParseContractAddressWrongPrefix :: ByteString -> ParseContractAddressError
+ Tezos.Address: ParseContractAddressWrongSize :: ByteString -> ParseContractAddressError
+ Tezos.Address: data ParseAddressRawError
+ Tezos.Address: instance Control.DeepSeq.NFData Tezos.Address.ParseAddressRawError
+ Tezos.Address: instance Formatting.Buildable.Buildable Tezos.Address.ParseAddressRawError
+ Tezos.Address: instance GHC.Classes.Eq Tezos.Address.ParseAddressRawError
+ Tezos.Address: instance GHC.Generics.Generic Tezos.Address.ParseAddressRawError
+ Tezos.Address: instance GHC.Show.Show Tezos.Address.ParseAddressRawError
+ Tezos.Address: parseAddressRaw :: ByteString -> Either ParseAddressRawError Address
+ Tezos.Address: parseContractHash :: Text -> Either ParseContractAddressError ContractHash
+ Tezos.Address: unsafeParseAddressRaw :: ByteString -> Address
+ Tezos.Core: oneMutez :: Mutez
+ Tezos.Core: zeroMutez :: Mutez
+ Tezos.Crypto: CryptoParseBinaryError :: Text -> CryptoParseError
+ Tezos.Crypto: instance Formatting.Buildable.Buildable Tezos.Crypto.ParseSignatureRawError
+ Tezos.Crypto: instance GHC.Classes.Eq Tezos.Crypto.ParseSignatureRawError
+ Tezos.Crypto: instance GHC.Generics.Generic Tezos.Crypto.ParseSignatureRawError
+ Tezos.Crypto: instance GHC.Show.Show Tezos.Crypto.ParseSignatureRawError
+ Tezos.Crypto: keyDecoders :: [TaggedDecoder PublicKey]
+ Tezos.Crypto: keyHashDecoders :: [TaggedDecoder KeyHash]
+ Tezos.Crypto: parseKeyHashRaw :: ByteString -> Either CryptoParseError KeyHash
+ Tezos.Crypto: parsePublicKeyRaw :: ByteString -> Either Text PublicKey
+ Tezos.Crypto: parseSignatureRaw :: ByteString -> Either ParseSignatureRawError Signature
+ Tezos.Crypto.P256: detSecretKeyDo :: MonadRandom m => m SecretKey
+ Tezos.Crypto.P256: sign :: MonadRandom m => SecretKey -> ByteString -> m Signature
+ Tezos.Crypto.Secp256k1: detSecretKeyDo :: MonadRandom m => m SecretKey
+ Tezos.Crypto.Secp256k1: newtype PublicKey
+ Tezos.Crypto.Util: CryptoParseBinaryError :: Text -> CryptoParseError
+ Tezos.Crypto.Util: mkPublicKey_ :: ByteArrayAccess ba => Curve -> ba -> Either CryptoParseError PublicKey
+ Tezos.Crypto.Util: mkSignature_ :: ByteArray ba => Curve -> ba -> Either CryptoParseError Signature
+ Tezos.Crypto.Util: publicKeyLengthBytes_ :: Integral n => Curve -> n
+ Tezos.Crypto.Util: publicKeyToBytes_ :: forall ba. (ByteArray ba, HasCallStack) => Curve -> PublicKey -> ba
+ Tezos.Crypto.Util: rnfCurve :: Curve -> ()
+ Tezos.Crypto.Util: signatureLengthBytes_ :: Integral n => Curve -> n
+ Tezos.Crypto.Util: signatureToBytes_ :: ByteArray ba => Curve -> Signature -> ba
+ Util.Binary: (#:) :: Word8 -> Get a -> TaggedDecoder a
+ Util.Binary: TaggedDecoder :: Word8 -> Get a -> TaggedDecoder a
+ Util.Binary: [tdDecoder] :: TaggedDecoder a -> Get a
+ Util.Binary: [tdTag] :: TaggedDecoder a -> Word8
+ Util.Binary: data TaggedDecoder a
+ Util.Binary: decodeBytesLike :: Buildable err => String -> (ByteString -> Either err a) -> Get a
+ Util.Binary: decodeWithTag :: String -> [TaggedDecoder a] -> Get a
+ Util.Binary: getByteStringCopy :: Int -> Get ByteString
+ Util.Binary: getRemainingByteStringCopy :: Get ByteString
+ Util.Binary: infixr 0 #:
+ Util.Binary: unknownTag :: String -> Word8 -> Get a
+ Util.CustomGeneric: cstr :: forall n. KnownNat n => [Natural] -> CstrDepth
+ Util.CustomGeneric: customGeneric :: String -> GenericStrategy -> Q [Dec]
+ Util.CustomGeneric: fld :: forall n. KnownNat n => Natural
+ Util.CustomGeneric: leftBalanced :: GenericStrategy
+ Util.CustomGeneric: leftComb :: GenericStrategy
+ Util.CustomGeneric: rightBalanced :: GenericStrategy
+ Util.CustomGeneric: rightComb :: GenericStrategy
+ Util.CustomGeneric: withDepths :: [CstrDepth] -> GenericStrategy
+ Util.Exception: DisplayExceptionInShow :: SomeException -> DisplayExceptionInShow
+ Util.Exception: [unDisplayExceptionInShow] :: DisplayExceptionInShow -> SomeException
+ Util.Exception: newtype DisplayExceptionInShow
+ Util.Named: infixl 4 <.?>
+ Util.Test.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary Michelson.Untyped.Contract.EntriesOrder
- Michelson.Doc: class (Typeable d, DOrd d, KnownNat (DocItemPosition d)) => DocItem d where {
+ Michelson.Doc: class (Typeable d, DOrd d) => DocItem d where {
- Michelson.Interpret: interpret :: ContractCode cp st -> EntryPointCallT cp arg -> Value arg -> Value st -> ContractEnv -> ContractReturn st
+ Michelson.Interpret: interpret :: ContractCode cp st -> EntrypointCallT cp arg -> Value arg -> Value st -> ContractEnv -> ContractReturn st
- Michelson.Macro: F :: (VarAnn, FieldAnn) -> PairStruct
+ Michelson.Macro: F :: FieldAnn -> PairStruct
- Michelson.Macro: UNPAIR :: PairStruct -> Macro
+ Michelson.Macro: UNPAIR :: UnpairStruct -> Macro
- Michelson.Macro: expandUnpapair :: InstrCallStack -> PairStruct -> [ExpandedOp]
+ Michelson.Macro: expandUnpapair :: InstrCallStack -> UnpairStruct -> [ExpandedOp]
- Michelson.Runtime: ContractState :: Mutez -> Value -> Contract -> Maybe SomeContract -> Maybe SomeValue -> ContractState
+ Michelson.Runtime: ContractState :: Mutez -> Contract cp st -> Value st -> ContractState
- Michelson.Runtime: [csContract] :: ContractState -> Contract
+ Michelson.Runtime: [csContract] :: ContractState -> Contract cp st
- Michelson.Runtime: [csStorage] :: ContractState -> Value
+ Michelson.Runtime: [csStorage] :: ContractState -> Value st
- Michelson.Runtime.GState: ContractState :: Mutez -> Value -> Contract -> Maybe SomeContract -> Maybe SomeValue -> ContractState
+ Michelson.Runtime.GState: ContractState :: Mutez -> Contract cp st -> Value st -> ContractState
- Michelson.Runtime.GState: [csContract] :: ContractState -> Contract
+ Michelson.Runtime.GState: [csContract] :: ContractState -> Contract cp st
- Michelson.Runtime.GState: [csStorage] :: ContractState -> Value
+ Michelson.Runtime.GState: [csStorage] :: ContractState -> Value st
- Michelson.TypeCheck: typeCheckContract :: TcOriginatedContracts -> Contract -> Either TCError SomeContract
+ Michelson.TypeCheck: typeCheckContract :: Contract -> Either TCError SomeContract
- Michelson.TypeCheck.Helpers: addImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> m (SomeInstr inp)
+ Michelson.TypeCheck.Helpers: addImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> ExpandedInstr -> m (SomeInstr inp)
- Michelson.TypeCheck.Helpers: arithImpl :: (Typeable (ArithRes aop n m : s), WellTyped (ArithRes aop n m), inp ~ (n : (m : s)), Monad t) => Instr inp (ArithRes aop n m : s) -> HST inp -> VarAnn -> t (SomeInstr inp)
+ Michelson.TypeCheck.Helpers: arithImpl :: forall aop inp m n s t. (ArithOp aop n m, Typeable (ArithRes aop n m : s), WellTyped (ArithRes aop n m), inp ~ (n : (m : s)), MonadReader InstrCallStack t, MonadError TCError t) => Instr inp (ArithRes aop n m : s) -> HST inp -> VarAnn -> ExpandedInstr -> t (SomeInstr inp)
- Michelson.TypeCheck.Helpers: edivImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> m (SomeInstr inp)
+ Michelson.TypeCheck.Helpers: edivImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> ExpandedInstr -> m (SomeInstr inp)
- Michelson.TypeCheck.Helpers: mulImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> m (SomeInstr inp)
+ Michelson.TypeCheck.Helpers: mulImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> ExpandedInstr -> m (SomeInstr inp)
- Michelson.TypeCheck.Helpers: subImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> m (SomeInstr inp)
+ Michelson.TypeCheck.Helpers: subImpl :: forall a b inp rs m. (Typeable rs, Each '[KnownT] [a, b], inp ~ (a : (b : rs)), MonadReader InstrCallStack m, MonadError TCError m) => Sing a -> Sing b -> HST inp -> VarAnn -> ExpandedInstr -> m (SomeInstr inp)
- Michelson.TypeCheck.Instr: typeCheckContract :: TcOriginatedContracts -> Contract -> Either TCError SomeContract
+ Michelson.TypeCheck.Instr: typeCheckContract :: Contract -> Either TCError SomeContract
- Michelson.TypeCheck.TypeCheck: TypeCheckEnv :: ~TcExtFrames -> ~ParameterType -> ~TcOriginatedContracts -> TypeCheckEnv
+ Michelson.TypeCheck.TypeCheck: TypeCheckEnv :: ~TcExtFrames -> ~TypeCheckMode -> TypeCheckEnv
- Michelson.TypeCheck.TypeCheck: runTypeCheck :: ParameterType -> TcOriginatedContracts -> TypeCheck a -> Either TCError a
+ Michelson.TypeCheck.TypeCheck: runTypeCheck :: TypeCheckMode -> TypeCheck a -> Either TCError a
- Michelson.TypeCheck.TypeCheck: type TcInstrHandler = forall inp. Typeable inp => ExpandedInstr -> HST inp -> TypeCheckInstr (SomeInstr inp)
+ Michelson.TypeCheck.TypeCheck: type TcInstrHandler = forall inp. (Typeable inp, HasCallStack) => ExpandedInstr -> HST inp -> TypeCheckInstr (SomeInstr inp)
- Michelson.TypeCheck.TypeCheck: type TcOriginatedContracts = Map ContractHash ParameterType
+ Michelson.TypeCheck.TypeCheck: type TcOriginatedContracts = Map ContractHash SomeParamType
- Michelson.TypeCheck.Types: getWTP :: forall t. SingI t => Maybe (Dict (WellTyped t))
+ Michelson.TypeCheck.Types: getWTP :: forall t. SingI t => Either NotWellTyped (Dict (WellTyped t))
- Michelson.TypeCheck.Value: typeCheckValImpl :: forall ty. SingI ty => TcInstrHandler -> Value -> TypeCheckInstr (Value ty)
+ Michelson.TypeCheck.Value: typeCheckValImpl :: forall ty. SingI ty => Maybe TcOriginatedContracts -> TcInstrHandler -> Value -> TypeCheckInstr (Value ty)
- Michelson.Typed.Haskell: ContractRef :: Address -> SomeEntryPointCall arg -> ContractRef (arg :: Type)
+ Michelson.Typed.Haskell: ContractRef :: Address -> SomeEntrypointCall arg -> ContractRef (arg :: Type)
- Michelson.Typed.Haskell: crDescriptionL :: forall a_a67wr. Lens' (ConstructorRep a_a67wr) (Maybe Text)
+ Michelson.Typed.Haskell: crDescriptionL :: forall a_a6ys3. Lens' (ConstructorRep a_a6ys3) (Maybe Text)
- Michelson.Typed.Haskell: crFieldsL :: forall a_a67wr a_a67xJ. Lens (ConstructorRep a_a67wr) (ConstructorRep a_a67xJ) [FieldRep a_a67wr] [FieldRep a_a67xJ]
+ Michelson.Typed.Haskell: crFieldsL :: forall a_a6ys3 a_a6ytl. Lens (ConstructorRep a_a6ys3) (ConstructorRep a_a6ytl) [FieldRep a_a6ys3] [FieldRep a_a6ytl]
- Michelson.Typed.Haskell: crNameL :: forall a_a67wr. Lens' (ConstructorRep a_a67wr) Text
+ Michelson.Typed.Haskell: crNameL :: forall a_a6ys3. Lens' (ConstructorRep a_a6ys3) Text
- Michelson.Typed.Haskell: frDescriptionL :: forall a_a67wq. Lens' (FieldRep a_a67wq) (Maybe Text)
+ Michelson.Typed.Haskell: frDescriptionL :: forall a_a6ys2. Lens' (FieldRep a_a6ys2) (Maybe Text)
- Michelson.Typed.Haskell: frNameL :: forall a_a67wq. Lens' (FieldRep a_a67wq) (Maybe Text)
+ Michelson.Typed.Haskell: frNameL :: forall a_a6ys2. Lens' (FieldRep a_a6ys2) (Maybe Text)
- Michelson.Typed.Haskell: frTypeRepL :: forall a_a67wq a_a67zu. Lens (FieldRep a_a67wq) (FieldRep a_a67zu) a_a67wq a_a67zu
+ Michelson.Typed.Haskell: frTypeRepL :: forall a_a6ys2 a_a6yv6. Lens (FieldRep a_a6ys2) (FieldRep a_a6yv6) a_a6ys2 a_a6yv6
- Michelson.Typed.Haskell.Doc: crDescriptionL :: forall a_a67wr. Lens' (ConstructorRep a_a67wr) (Maybe Text)
+ Michelson.Typed.Haskell.Doc: crDescriptionL :: forall a_a6ys3. Lens' (ConstructorRep a_a6ys3) (Maybe Text)
- Michelson.Typed.Haskell.Doc: crFieldsL :: forall a_a67wr a_a67xJ. Lens (ConstructorRep a_a67wr) (ConstructorRep a_a67xJ) [FieldRep a_a67wr] [FieldRep a_a67xJ]
+ Michelson.Typed.Haskell.Doc: crFieldsL :: forall a_a6ys3 a_a6ytl. Lens (ConstructorRep a_a6ys3) (ConstructorRep a_a6ytl) [FieldRep a_a6ys3] [FieldRep a_a6ytl]
- Michelson.Typed.Haskell.Doc: crNameL :: forall a_a67wr. Lens' (ConstructorRep a_a67wr) Text
+ Michelson.Typed.Haskell.Doc: crNameL :: forall a_a6ys3. Lens' (ConstructorRep a_a6ys3) Text
- Michelson.Typed.Haskell.Doc: frDescriptionL :: forall a_a67wq. Lens' (FieldRep a_a67wq) (Maybe Text)
+ Michelson.Typed.Haskell.Doc: frDescriptionL :: forall a_a6ys2. Lens' (FieldRep a_a6ys2) (Maybe Text)
- Michelson.Typed.Haskell.Doc: frNameL :: forall a_a67wq. Lens' (FieldRep a_a67wq) (Maybe Text)
+ Michelson.Typed.Haskell.Doc: frNameL :: forall a_a6ys2. Lens' (FieldRep a_a6ys2) (Maybe Text)
- Michelson.Typed.Haskell.Doc: frTypeRepL :: forall a_a67wq a_a67zu. Lens (FieldRep a_a67wq) (FieldRep a_a67zu) a_a67wq a_a67zu
+ Michelson.Typed.Haskell.Doc: frTypeRepL :: forall a_a6ys2 a_a6yv6. Lens (FieldRep a_a6ys2) (FieldRep a_a6yv6) a_a6ys2 a_a6yv6
- Michelson.Typed.Haskell.Value: ContractRef :: Address -> SomeEntryPointCall arg -> ContractRef (arg :: Type)
+ Michelson.Typed.Haskell.Value: ContractRef :: Address -> SomeEntrypointCall arg -> ContractRef (arg :: Type)
- Michelson.Typed.Instr: Contract :: ContractCode cp st -> ParamNotes cp -> Notes st -> Contract cp st
+ Michelson.Typed.Instr: Contract :: ContractCode cp st -> ParamNotes cp -> Notes st -> EntriesOrder -> Contract cp st
- Michelson.Typed.Instr: [SELF] :: forall (arg :: T) s. ParameterScope arg => SomeEntryPointCallT arg -> Instr s ('TContract arg : s)
+ Michelson.Typed.Instr: [SELF] :: forall (arg :: T) s. ParameterScope arg => SomeEntrypointCallT arg -> Instr s ('TContract arg : s)
- Michelson.Typed.Value: [VContract] :: forall arg instr. Address -> SomeEntryPointCallT arg -> Value' instr ('TContract arg)
+ Michelson.Typed.Value: [VContract] :: forall arg instr. Address -> SomeEntrypointCallT arg -> Value' instr ('TContract arg)
- Michelson.Typed.Value: liftCallArg :: EntryPointCallT param arg -> Value' instr arg -> Value' instr param
+ Michelson.Typed.Value: liftCallArg :: EntrypointCallT param arg -> Value' instr arg -> Value' instr param
- Michelson.Untyped.Contract: Contract :: ParameterType -> Storage -> [op] -> Contract' op
+ Michelson.Untyped.Contract: Contract :: ParameterType -> Storage -> [op] -> EntriesOrder -> Contract' op
- Morley.Micheline.Class: fromExpression :: FromExpression a => Expression -> Maybe a
+ Morley.Micheline.Class: fromExpression :: FromExpression a => Expression -> Either FromExpressionError a
- Tezos.Crypto.P256: PublicKey :: ByteString -> PublicKey
+ Tezos.Crypto.P256: PublicKey :: PublicKey -> PublicKey
- Tezos.Crypto.P256: Signature :: ByteString -> Signature
+ Tezos.Crypto.P256: Signature :: Signature -> Signature
- Tezos.Crypto.P256: [unPublicKey] :: PublicKey -> ByteString
+ Tezos.Crypto.P256: [unPublicKey] :: PublicKey -> PublicKey
- Tezos.Crypto.P256: [unSignature] :: Signature -> ByteString
+ Tezos.Crypto.P256: [unSignature] :: Signature -> Signature
- Tezos.Crypto.Secp256k1: PublicKey :: PublicKey -> Maybe ByteString -> PublicKey
+ Tezos.Crypto.Secp256k1: PublicKey :: PublicKey -> PublicKey
Files
- CHANGES.md +44/−8
- README.md +1/−7
- app/Main.hs +16/−16
- app/REPL.hs +233/−0
- bench/Main.hs +0/−74
- morley.cabal +14/−135
- src/Michelson/Doc.hs +15/−13
- src/Michelson/Doc/Test.hs +0/−243
- src/Michelson/Interpret.hs +14/−21
- src/Michelson/Interpret/Pack.hs +6/−9
- src/Michelson/Interpret/Unpack.hs +40/−127
- src/Michelson/Macro.hs +53/−25
- src/Michelson/OpSize.hs +1/−1
- src/Michelson/Parser.hs +54/−15
- src/Michelson/Parser/Macro.hs +16/−9
- src/Michelson/Parser/Value.hs +6/−5
- src/Michelson/Runtime.hs +15/−44
- src/Michelson/Runtime/GState.hs +65/−87
- src/Michelson/Test.hs +0/−113
- src/Michelson/Test/Dummy.hs +0/−62
- src/Michelson/Test/Gen.hs +0/−128
- src/Michelson/Test/Import.hs +0/−192
- src/Michelson/Test/Integrational.hs +0/−601
- src/Michelson/Test/Unit.hs +0/−135
- src/Michelson/Test/Util.hs +0/−251
- src/Michelson/Text.hs +2/−10
- src/Michelson/TypeCheck.hs +4/−2
- src/Michelson/TypeCheck/Error.hs +6/−6
- src/Michelson/TypeCheck/Helpers.hs +29/−11
- src/Michelson/TypeCheck/Instr.hs +86/−62
- src/Michelson/TypeCheck/TypeCheck.hs +64/−19
- src/Michelson/TypeCheck/Types.hs +57/−38
- src/Michelson/TypeCheck/Value.hs +188/−176
- src/Michelson/Typed.hs +1/−1
- src/Michelson/Typed/Arith.hs +38/−0
- src/Michelson/Typed/Convert.hs +3/−1
- src/Michelson/Typed/EntryPoints.hs +0/−478
- src/Michelson/Typed/Entrypoints.hs +512/−0
- src/Michelson/Typed/Haskell/Doc.hs +21/−6
- src/Michelson/Typed/Haskell/Instr/Sum.hs +1/−1
- src/Michelson/Typed/Haskell/Value.hs +11/−20
- src/Michelson/Typed/Instr.hs +24/−4
- src/Michelson/Typed/Polymorphic.hs +23/−0
- src/Michelson/Typed/Util.hs +27/−6
- src/Michelson/Typed/Value.hs +4/−4
- src/Michelson/Untyped.hs +1/−1
- src/Michelson/Untyped/Contract.hs +94/−7
- src/Michelson/Untyped/EntryPoints.hs +0/−128
- src/Michelson/Untyped/Entrypoints.hs +128/−0
- src/Michelson/Untyped/Type.hs +1/−1
- src/Michelson/Untyped/Value.hs +1/−1
- src/Morley/Micheline/Binary.hs +3/−2
- src/Morley/Micheline/Class.hs +60/−28
- src/Morley/Micheline/Expression.hs +23/−47
- src/Tezos/Address.hs +99/−60
- src/Tezos/Core.hs +9/−9
- src/Tezos/Crypto.hs +74/−37
- src/Tezos/Crypto/Ed25519.hs +0/−18
- src/Tezos/Crypto/P256.hs +53/−60
- src/Tezos/Crypto/Secp256k1.hs +16/−102
- src/Tezos/Crypto/Util.hs +119/−5
- src/Util/Binary.hs +67/−1
- src/Util/CustomGeneric.hs +381/−0
- src/Util/Exception.hs +2/−1
- src/Util/Named.hs +3/−8
- src/Util/Test/Arbitrary.hs +6/−4
- src/Util/Test/Gen.hs +0/−317
- src/Util/Test/Ingredients.hs +0/−18
- test/Main.hs +0/−16
- test/Test/Analyzer.hs +0/−56
- test/Test/EntryPoints.hs +0/−96
- test/Test/Ext.hs +0/−101
- test/Test/Integrational.hs +0/−36
- test/Test/Interpreter.hs +0/−333
- test/Test/Interpreter/Apply.hs +0/−39
- test/Test/Interpreter/Auction.hs +0/−142
- test/Test/Interpreter/Balance.hs +0/−107
- test/Test/Interpreter/CallSelf.hs +0/−111
- test/Test/Interpreter/ComparableSet.hs +0/−67
- test/Test/Interpreter/Compare.hs +0/−61
- test/Test/Interpreter/ComparePairs.hs +0/−64
- test/Test/Interpreter/Conditionals.hs +0/−62
- test/Test/Interpreter/ContractOp.hs +0/−77
- test/Test/Interpreter/EnvironmentSpec.hs +0/−122
- test/Test/Interpreter/StackRef.hs +0/−37
- test/Test/Interpreter/StringCaller.hs +0/−140
- test/Test/Macro.hs +0/−193
- test/Test/Michelson/Runtime.hs +0/−229
- test/Test/Michelson/Text.hs +0/−164
- test/Test/Michelson/Typed/Util.hs +0/−35
- test/Test/Michelson/Typed/Value.hs +0/−35
- test/Test/Morley/Expression.hs +0/−167
- test/Test/OpSize.hs +0/−277
- test/Test/Optimizer.hs +0/−163
- test/Test/Parser.hs +0/−312
- test/Test/Preprocess.hs +0/−60
- test/Test/Printer/Michelson.hs +0/−131
- test/Test/Serialization/Aeson.hs +0/−52
- test/Test/Serialization/Michelson.hs +0/−1093
- test/Test/Tezos/Address.hs +0/−54
- test/Test/Tezos/Core.hs +0/−80
- test/Test/Tezos/Crypto.hs +0/−217
- test/Test/Tezos/Crypto/Hash.hs +0/−52
- test/Test/Typeable.hs +0/−36
- test/Test/Typecheck.hs +0/−287
- test/Test/Untyped/Instr.hs +0/−47
- test/Test/Util/Contracts.hs +0/−83
- test/Test/Util/HUnit.hs +0/−18
- test/Test/Util/Hedgehog.hs +0/−91
- test/Test/Util/Parser.hs +0/−21
- test/Test/ValConversion.hs +0/−73
- test/Tree.hs +0/−5
CHANGES.md view
@@ -1,6 +1,42 @@-Unreleased-==========-<!-- Append new entries here -->+1.5.0+=====+* [!509](https://gitlab.com/morley-framework/morley/-/merge_requests/509)+ Implement missing parts of `secp256k1` and `P256`.+ + `checkSignature` now works correctly with all signature formats.+* [!511](https://gitlab.com/morley-framework/morley/-/merge_requests/511)+ Add `zeroMutez` and `oneMutez` for safe initial values.+* [!500](https://gitlab.com/morley-framework/morley/-/merge_requests/500)+ Add ability to parse optimized formats of `signature`, `contract`, `key`, and `key_hash`.+* [!486](https://gitlab.com/morley-framework/morley/-/merge_requests/486)+ Add `Util.CustomGeneric` to derive `Generic` instances with a custom tree structure.+* [!466](https://gitlab.com/morley-framework/morley/-/merge_requests/466)+ Add sub-command 'repl' to Morley executable which starts an REPL that+ can execute Morley instructions.+* [!492](https://gitlab.com/morley-framework/morley/-/merge_requests/492)+ + `TypeHasDoc` instance for `newtype` now has its field remove by default+ without having to use `haskellRepNoFields`.+ + Add `haskellAddNewtypeField` function to add field name for `newtype`+ if desired.+* [!487](https://gitlab.com/morley-framework/morley/-/merge_requests/487)+ Fix typechecking error message pointing to the whole `map unit nat` as not comprable.+ + It now only points to `unit` type as not comparable.+* [!457](https://gitlab.com/morley-framework/morley/-/merge_requests/457)+ + Allow using the same position when defining `DocItem`.+ + When two `DocItem`s have the same position, they will be sorted alphabetically by their names.+* [!480](https://gitlab.com/morley-framework/morley/-/merge_requests/480)+ Arithmetic instructions (ADD, MUL, EDIV, LSL, ...) now take annotations into account.+ + Results from the instructions are also properly annotated.+* [!470](https://gitlab.com/morley-framework/morley/-/merge_requests/470)+ Move `Michelson.Doc.Test`, `Michelson.Test` and Hedgehog generators to+ a new package: `cleveland`.+* [!453](https://gitlab.com/morley-framework/morley/-/merge_requests/453)+ Order of top-level entries in contract are now preserved.+ + `PACK`/`UNPACK` now serialize contracts according their top-level entries order.+ + Pretty printer now display contracts according to their given top-level entries order.+* [!445](https://gitlab.com/morley-framework/morley/merge_requests/445)+ Remove unnecessary `TcOriginatedContracts` usages and split `typeCheckTopLevelValue`+ to `typeCheckParameter` (the only case where `TcOriginatedContracts` is useful)+ and `typeCheckStorage`. 1.4.0 =====@@ -136,12 +172,12 @@ * [!241](https://gitlab.com/morley-framework/morley/merge_requests/241) Derive `Show` instance for `Void_`. * [!238](https://gitlab.com/morley-framework/morley/merge_requests/238)- + `NoExplicitDefaultEntryPoint` is now GHC-understandable constraint,+ + `NoExplicitDefaultEntrypoint` is now GHC-understandable constraint, for previous behaviour with nice error message use- `ForbidExplicitDefaultEntryPoint`.- + `CanHaveEntryPoints` is made stricter, now it returns true only for+ `ForbidExplicitDefaultEntrypoint`.+ + `CanHaveEntrypoints` is made stricter, now it returns true only for Michelson sum types. If that's a problem for you, consider using- `ShouldHaveEntryPoints` wrapper.+ `ShouldHaveEntrypoints` wrapper. + Added pseudo `Empty` type. * [!219](https://gitlab.com/morley-framework/morley/merge_requests/219) Various entrypoints-related updates.@@ -155,7 +191,7 @@ + Generally it is encouraged to use `TAddress` instead of `Address` where it is used to refer to a contract. + `fromContractAddr` renamed to `fromContractRef`.- + `Default` instances for `{Some,}EntryPointCall` are removed, use+ + `Default` instances for `{Some,}EntrypointCall` are removed, use one of explicit versions: `epcPrimitive`, `epcCallRootUnsafe`. + Reworked `FutureContract` type.
README.md view
@@ -35,13 +35,6 @@ 1. If your contract is called `foo.mtz`, use `morley print --contract foo.mtz --output foo.tz`. Note that normally you should not use `morley` directly, you should use `morley.sh` or `stack exec -- morley`. See usage instructions below. 2. After that, you can use existing Tezos tools to deploy your contract. You can also typecheck or interpret it using a reference implementation. If you are not familiar with the Tezos tooling, please read [Tezos documentation](https://tezos.gitlab.io/index.html) or [Michelson tutorial](https://gitlab.com/morley-framework/michelson-tutorial). -## IV: Testing EDSL--Another way to test Michelson contracts is to write tests in Haskell using the testing EDSL provided by Morley.-It supports both integrational and unit tests.-Tests of both types can use static data or arbitrary data.-There is [a document](/docs/testingEDSL.md) with a detailed description of the EDSL and a tutorial about its usage.- ## Running and building Morley executable provides the following functionality:@@ -59,6 +52,7 @@ * Get [script](/scripts/morley.sh) (e. g. using `curl https://gitlab.com/morley-framework/morley/raw/master/scripts/morley.sh > morley.sh`) and run it `./morley.sh <args>`. This script will pull a docker image that contains the latest version of Morley executable from the master branch and run it with the given arguments.+ You'll need `coreutils` to be installed in order to use `morley.sh`. * Usage example: + `./morley.sh` to see help message + `./morley.sh run --contract add1.tz --storage 1 --parameter 1 --amount 1`
app/Main.hs view
@@ -23,13 +23,14 @@ import Michelson.Optimizer (optimize) import Michelson.Printer (printSomeContract, printUntypedContract) import Michelson.Runtime- (TxData(..), originateContract, prepareContract, readAndParseContract, runContract, transfer,- typeCheckWithDb)+ (TxData(..), originateContract, prepareContract, readAndParseContract, runContract, transfer) import Michelson.Runtime.GState (genesisAddress)+import Michelson.TypeCheck (typeCheckContract) import Michelson.TypeCheck.Types (SomeContract(..), mapSomeContract) import Michelson.Typed (Contract(..)) import qualified Michelson.Untyped as U import Morley.CLI+import REPL import Tezos.Address (Address) import Tezos.Core (Mutez, Timestamp(..), unsafeMkMutez) import Tezos.Crypto@@ -51,22 +52,20 @@ | Run RunOptions | Originate OriginateOptions | Transfer TransferOptions+ | REPL data OptimizeOptions = OptimizeOptions { optoContractFile :: Maybe FilePath- , optoDBPath :: FilePath , optoOutput :: Maybe FilePath , optoSingleLine :: Bool } data AnalyzeOptions = AnalyzeOptions { aoContractFile :: Maybe FilePath- , aoDBPath :: FilePath } data TypeCheckOptions = TypeCheckOptions { tcoContractFile :: Maybe FilePath- , tcoDBPath :: FilePath , tcoVerbose :: Bool } @@ -111,7 +110,8 @@ originateSubCmd <> transferSubCmd <> optimizeSubCmd <>- analyzeSubCmd+ analyzeSubCmd <>+ replSubCmd where mkCommandParser commandName parser desc = command commandName $@@ -142,6 +142,11 @@ "Run passed contract. \ \It's originated first and then a transaction is sent to it" + replSubCmd =+ mkCommandParser "repl"+ (pure REPL)+ "Start a Morley REPL"+ originateSubCmd = mkCommandParser "originate" (Originate <$> originateOptions)@@ -181,7 +186,6 @@ typeCheckOptions :: Opt.Parser TypeCheckOptions typeCheckOptions = TypeCheckOptions <$> optional contractFileOption- <*> dbPathOption <*> verboseFlag parseOptions :: Opt.Parser (Maybe FilePath, Bool)@@ -197,14 +201,11 @@ optimizeOptions :: Opt.Parser OptimizeOptions optimizeOptions = OptimizeOptions <$> optional contractFileOption- <*> dbPathOption <*> outputOption <*> onelineOption analyzeOptions :: Opt.Parser AnalyzeOptions- analyzeOptions = AnalyzeOptions- <$> optional contractFileOption- <*> dbPathOption+ analyzeOptions = AnalyzeOptions <$> optional contractFileOption runOptions :: Opt.Parser RunOptions runOptions =@@ -288,19 +289,17 @@ write $ printUntypedContract forceSingleLine contract Optimize OptimizeOptions{..} -> do untypedContract <- prepareContract optoContractFile- checkedContract <- either throwM pure =<<- typeCheckWithDb optoDBPath untypedContract+ checkedContract <- either throwM pure $ typeCheckContract untypedContract let optimizedContract = mapSomeContract optimize checkedContract let write = maybe putStrLn writeFileUtf8 optoOutput write $ printSomeContract optoSingleLine optimizedContract Analyze AnalyzeOptions{..} -> do untypedContract <- prepareContract aoContractFile- SomeContract contract <- either throwM pure =<<- typeCheckWithDb aoDBPath untypedContract+ SomeContract contract <- either throwM pure $ typeCheckContract untypedContract putTextLn $ pretty $ analyze (cCode contract) TypeCheck TypeCheckOptions{..} -> do morleyContract <- prepareContract tcoContractFile- either throwM (const pass) =<< typeCheckWithDb tcoDBPath morleyContract+ either throwM (const pass) $ typeCheckContract morleyContract putTextLn "Contract is well-typed" Run RunOptions {..} -> do michelsonContract <- prepareContract roContractFile@@ -322,6 +321,7 @@ transfer toNow toMaxSteps toDBPath toDestination toTxData ! #verbose toVerbose ! #dryRun toDryRun+ REPL -> runRepl usageDoc :: Maybe Doc
+ app/REPL.hs view
@@ -0,0 +1,233 @@+-- SPDX-FileCopyrightText: 2020 Tocqueville Group+--+-- SPDX-License-Identifier: LicenseRef-MIT-TQ+{-|+Implements an REPL that can execute Morley instructions. REPL starts with an+empty stack. At each instruction entered, the modified stack, or an error is+printed. Multiple instructions separated by semicolons should work as expected.+Available meta commads are:++ ':help', displays this help\n ':stack', prints the current stack+ ':loadstack filename', loads the stack from a file+ ':dumpstack filename', dumps the stack to a file+ ':clear', clears the current stack. Ctrl-D or ':quit' to end REPL."+-}+module REPL+ ( runRepl+ ) where++import Data.List (stripPrefix)+import Data.Text (strip)+import Data.Typeable (cast)+import Data.Vinyl (Rec(..))+import Fmt (pretty)+import System.Console.Haskeline+import Text.Megaparsec (parse)+import qualified Data.Aeson as Aeson+import qualified Data.ByteString.Lazy as BSL++import Michelson.Interpret (ContractEnv(..), interpretInstr)+import qualified Michelson.Untyped as U+import qualified Michelson.Typed as T+import Michelson.Macro (ParsedOp, expandList)+import Michelson.Parser (errorBundlePretty, parseExpandValue, ops, type_)+import Michelson.Parser.Types (noLetEnv)+import Michelson.Printer (printDoc, printTypedValue)+import Michelson.Printer.Util (doesntNeedParens)+import Michelson.Runtime.GState (genesisAddress)+import Michelson.TypeCheck.Types (HST(..), NotWellTyped(..))+import Michelson.TypeCheck.Instr (typeCheckList, typeCheckParameter)+import Michelson.TypeCheck (SomeInstr(..), SomeInstrOut(..), getWTP, runTypeCheckIsolated)+import Tezos.Core (Timestamp(..), dummyChainId, unsafeMkMutez)+import Util.IO++data SomeStack = forall t. Typeable t => SomeStack+ { stValues :: (Rec T.Value t)+ , stTypes :: HST t+ }++type ReplM = InputT (StateT SomeStack IO)++runRepl :: IO ()+runRepl = evalStateT (runInputT defaultSettings repl) emptyStack++-- | Prints an error message to stderr+printErr :: (MonadIO m) => Text -> m ()+printErr m = liftIO $ hPutStrLn stderr m++-- | Read commands or instructions from stdin and executes them.+-- If user presses Ctrl-c during execution of an instruction, it will be+-- caught and handled here itself. This ensures that REPL does not crash as+-- a result of user pressing Ctrl-c in an attempt to end a loop.+repl :: ReplM ()+repl = do+ minput <- getInputLine "Morley>>> "+ case strStrip <$> minput of+ Nothing -> pass -- Ctrl D+ Just ":quit" -> pass+ Just input -> do+ case input of+ "" -> pass+ ":clear" -> lift $ put emptyStack+ ":stack" -> printStack+ (stripPrefix ":dumpstack" -> Just filename) ->+ dumpStackToFile $ toString $ strip $ toText filename+ (stripPrefix ":loadstack" -> Just filename) ->+ loadStackFromFile $ toString $ strip $ toText filename+ ":help" -> printHelp+ (stripPrefix ":" -> Just cmd) ->+ printErr $ "Unknown command`"<> (toText cmd) <> "`. Use :help to see a list of commands"+ _ -> handle (\Interrupt -> putTextLn "Cancelled") $ withInterrupt $ runCode (toText input)+ repl+ where+ strStrip = toString . strip . toText++-- | Try to execute the given input string as a Morley instruction.+runCode :: Text -> ReplM ()+runCode code = do+ case parseInstructions code of+ Left err -> printErr err+ Right [] -> pass+ Right parsedOps -> do+ let expandedOps = expandList parsedOps+ lift get >>= \case+ SomeStack {..} -> case cast stTypes of+ Just hstInp -> case runTypeCheckIsolated $ typeCheckList expandedOps hstInp of+ Right someInstr -> do+ case someInstr of+ _ :/ (instr ::: hstOut)-> case interpretInstr dummyContractEnv instr stValues of+ Right recOut -> do+ lift $ put (SomeStack recOut hstOut)+ printStack+ Left michelsonFail -> putTextLn (pretty michelsonFail)+ _ :/ (AnyOutInstr _) -> putTextLn "Encountered a FAILWITH instruction"+ Left err -> printErr $ show err+ Nothing -> printErr "Casting stack failed"++dummyContractEnv :: ContractEnv+dummyContractEnv = ContractEnv+ { ceNow = Timestamp 100+ , ceMaxSteps = 100500+ , ceBalance = unsafeMkMutez 100+ , ceContracts = mempty+ , ceSelf = genesisAddress+ , ceSource = genesisAddress+ , ceSender = genesisAddress+ , ceAmount = unsafeMkMutez 100+ , ceChainId = dummyChainId+ , ceOperationHash = Nothing+ }++printHelp :: ReplM ()+printHelp = putTextLn "REPL starts with an empty stack. At each instruction entered,\+ \ the modified stack, or an error is printed. Multiple instructions separated by semicolons should work\+ \ as expected. Available meta commads are:\n\+ \ ':help', displays this help\n ':stack', prints the current stack\n\+ \ ':loadstack filename', loads the stack from a file\n\+ \ ':dumpstack filename', dumps the stack to a file \n\+ \ ':clear', clears the current stack. Ctrl-D or ':quit' to end REPL."++printStack :: ReplM ()+printStack = lift get >>= \case+ SomeStack stk hst -> do+ putTextLn "--"+ putTextLn $ showStack stk hst++emptyStack :: SomeStack+emptyStack = SomeStack RNil SNil++parseInstructions :: Text -> Either Text [ParsedOp]+parseInstructions src = let+ codeParser = runReaderT ops noLetEnv+ in case parse codeParser "" src of+ Right p -> Right p+ Left err -> Left (toText $ errorBundlePretty err)++-- helpers++addValueToHST+ :: forall t xs. Typeable xs+ => T.Value t+ -> HST xs+ -> Either Text (T.Dict (Typeable (t ': xs)), HST (t ': xs))+addValueToHST v hstIn = case T.valueTypeSanity v of+ T.Dict -> case getWTP @t of+ Right wtpDict -> Right (T.Dict, (T.starNotes @t, wtpDict, U.noAnn) ::& hstIn)+ Left (NotWellTyped t) -> let+ U.Type t' tann = T.toUType t+ in Left $ "Value of type '" <> (renderT t' (U.fullAnnSet [tann] [] []))+ <> "' is not well typed in the provided Value."++renderType :: T.SingI t => T.Notes t -> U.VarAnn -> Text+renderType notes vann = let+ U.Type t tann = T.mkUType notes+ in renderT t (U.fullAnnSet [tann] [] [vann])++renderT :: U.T -> U.AnnotationSet -> Text+renderT t annSet = toText $ printDoc True $ U.renderType t True doesntNeedParens annSet++dumpStackToFile :: FilePath -> ReplM ()+dumpStackToFile fname =+ lift get >>= \case+ SomeStack stk hst -> case dumpStack stk hst of+ Right stkd -> liftIO $ Prelude.catch (writeFileUtf8 fname $ Aeson.encode stkd) handler+ Left err -> printErr err+ where+ handler :: IOException -> IO ()+ handler e = printErr $ toText $ displayException e++loadStackFromFile :: FilePath -> ReplM ()+loadStackFromFile fname = do+ mStack <- liftIO $ flip Prelude.catch handler $ do+ stackTxt <- BSL.readFile fname+ case Aeson.decode @[(Text, Text)] stackTxt of+ Just s -> pure $ loadStack s+ Nothing -> pure $ Left "Decoding error when parsing stack data from file"+ case mStack of+ Right stk -> lift $ put stk+ Left err -> printErr err+ where+ handler :: IOException -> IO (Either Text a)+ handler e = pure $ Left $ toText $ displayException e++-- | Dump stack as a lit of tuples that represent (value, type) pairs.+dumpStack :: forall t. Rec T.Value t -> HST t -> Either Text [(Text, Text)]+dumpStack RNil _ = Right []+dumpStack ((v :: T.Value a) :& rst) ((notes, _, vann) ::& rHst) = case T.valueTypeSanity v of+ T.Dict -> case T.checkOpPresence (T.sing @a) of+ T.OpAbsent -> case dumpStack rst rHst of+ Right t -> Right ((toText $ printTypedValue True v, renderType notes vann) : t)+ Left e -> Left e+ T.OpPresent -> Left "Cannot dump stack with operations"++-- | Overwrite the current stack with a stack loaded from a list of tuples+-- representing (value/type) pairs.+loadStack :: [(Text, Text)] -> Either Text SomeStack+loadStack = foldl' buildStack (Right emptyStack) . reverse+ where+ buildStack :: Either Text SomeStack -> (Text, Text) -> Either Text SomeStack+ buildStack (Left err) _ = Left err+ buildStack (Right (SomeStack stk hst)) (txVal, txTyp) = case+ (parseExpandValue txVal, parse (runReaderT type_ noLetEnv) "" txTyp) of+ (Right val, Right typ) ->+ case typeCheckParameter mempty typ val of+ Right (T.SomeValue tVal) ->+ case addValueToHST tVal hst of+ Right (T.Dict, newHst) -> Right $ SomeStack (tVal :& stk) newHst+ Left err -> Left err+ Left tcError -> Left $ show tcError+ (Left err, _) -> Left $ pretty err+ (_, Left err) -> Left $ toText $ errorBundlePretty err++showStack :: forall t. Rec T.Value t -> HST t -> Text+showStack RNil _ = "--"+showStack ((v :: T.Value a) :& rst) ((notes, _, vann) ::& rHst) = case T.valueTypeSanity v of+ T.Dict -> case T.checkOpPresence (T.sing @a) of+ T.OpAbsent -> -- print nice if value has no operations+ addSuffix (toText $ printTypedValue True v)+ T.OpPresent -> -- else just call show, and indicate value has operation inside+ case (v, T.sing @a) of+ (T.VList [], T.STList T.STOperation) -> "{ } :: list operation\n" <> showStack rst rHst+ _ -> addSuffix $ "(operations container:" <> show v <> ")"+ where+ addSuffix val = val <>" :: " <> renderType notes vann <> "\n" <> showStack rst rHst
− bench/Main.hs
@@ -1,74 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--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.cCode basicC) T.epcPrimitive T.VUnit (T.VList [T.VInt 0]))- dummyContractEnv- )-- dummyAddress = detGenKeyAddress "thegreatandpowerful"- dummyString = mkMTextUnsafe "TGAP"- stringCallerBench = bench stringCallerFp- (nf- (interpret (T.cCode stringCallerC) T.epcPrimitive (T.toVal dummyString) (T.toVal dummyAddress))- dummyContractEnv- )- callSelfBench = bench callSelfFp- (nf- (interpret (T.cCode callSelfC) T.epcPrimitive (T.toVal (100 :: Integer)) (T.toVal (0 :: Natural)))- dummyContractEnv- )-- sq2Bench = bench sq2Fp- (nf- (interpret (T.cCode 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
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 6cecee7b4a0dcb06694b15ff8be6902535e2f0b301c8c28cc603468df87e3153+-- hash: baa867af7f9b3c6cbf4de3c7d9746427b24b2cca6f9c9cb9025ad098b40ef60e name: morley-version: 1.4.0+version: 1.5.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@@ -31,7 +31,6 @@ exposed-modules: Michelson.Analyzer Michelson.Doc- Michelson.Doc.Test Michelson.ErrorPos Michelson.FailPattern Michelson.Interpret@@ -59,13 +58,6 @@ Michelson.Runtime Michelson.Runtime.GState Michelson.Runtime.TxData- Michelson.Test- Michelson.Test.Dummy- Michelson.Test.Gen- Michelson.Test.Import- Michelson.Test.Integrational- Michelson.Test.Unit- Michelson.Test.Util Michelson.Text Michelson.TypeCheck Michelson.TypeCheck.Error@@ -81,7 +73,7 @@ Michelson.Typed.Arith Michelson.Typed.Convert Michelson.Typed.Doc- Michelson.Typed.EntryPoints+ Michelson.Typed.Entrypoints Michelson.Typed.Extract Michelson.Typed.Haskell Michelson.Typed.Haskell.Doc@@ -103,7 +95,7 @@ Michelson.Untyped.Aliases Michelson.Untyped.Annotation Michelson.Untyped.Contract- Michelson.Untyped.EntryPoints+ Michelson.Untyped.Entrypoints Michelson.Untyped.Ext Michelson.Untyped.Instr Michelson.Untyped.Type@@ -127,6 +119,7 @@ Util.Binary Util.ByteString Util.CLI+ Util.CustomGeneric Util.Default Util.Exception Util.Fcf@@ -141,8 +134,6 @@ Util.Peano Util.Positive Util.Test.Arbitrary- Util.Test.Gen- Util.Test.Ingredients Util.Text Util.TH Util.Type@@ -159,30 +150,23 @@ default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DerivingVia EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances UndecidableSuperClasses 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:- HUnit- , QuickCheck+ QuickCheck , aeson , aeson-casing , aeson-pretty , base >=4.7 && <5- , base16-bytestring , base58-bytestring , binary , bytestring , constraints >=0.11 , containers- , criterion , cryptonite , data-default , first-class-families >=0.5.0.0 , fmt- , formatting- , ghc-prim+ , generic-deriving , gitrev- , hedgehog , hex-text- , hspec- , hspec-expectations , interpolate , lens , megaparsec >=7.0.0@@ -190,7 +174,6 @@ , morley-prelude >=0.3.0 , mtl , named- , o-clock , optparse-applicative , parser-combinators >=1.0.0 , quickcheck-arbitrary-adt@@ -198,13 +181,7 @@ , semigroups >=0.19.1 , show-type , singletons- , statistics , syb- , tasty- , tasty-ant-xml- , tasty-hedgehog- , tasty-hunit-compat- , tasty-quickcheck , template-haskell , text , th-lift@@ -223,6 +200,7 @@ executable morley main-is: Main.hs other-modules:+ REPL Paths_morley autogen-modules: Paths_morley@@ -231,118 +209,19 @@ default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DerivingVia EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances UndecidableSuperClasses 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 >=4.7 && <5- , fmt- , morley- , morley-prelude- , named- , optparse-applicative- , pretty-simple- mixins:- base hiding (Prelude)- default-language: Haskell2010--test-suite morley-test- type: exitcode-stdio-1.0- main-is: Main.hs- other-modules:- Test.Analyzer- Test.EntryPoints- Test.Ext- Test.Integrational- Test.Interpreter- Test.Interpreter.Apply- Test.Interpreter.Auction- Test.Interpreter.Balance- Test.Interpreter.CallSelf- Test.Interpreter.ComparableSet- Test.Interpreter.Compare- Test.Interpreter.ComparePairs- Test.Interpreter.Conditionals- Test.Interpreter.ContractOp- Test.Interpreter.EnvironmentSpec- Test.Interpreter.StackRef- Test.Interpreter.StringCaller- Test.Macro- Test.Michelson.Runtime- Test.Michelson.Text- Test.Michelson.Typed.Util- Test.Michelson.Typed.Value- Test.Morley.Expression- Test.OpSize- Test.Optimizer- Test.Parser- Test.Preprocess- Test.Printer.Michelson- Test.Serialization.Aeson- Test.Serialization.Michelson- Test.Tezos.Address- Test.Tezos.Core- Test.Tezos.Crypto- Test.Tezos.Crypto.Hash- Test.Typeable- Test.Typecheck- Test.Untyped.Instr- Test.Util.Contracts- Test.Util.Hedgehog- Test.Util.HUnit- Test.Util.Parser- Test.ValConversion- Tree- Paths_morley- hs-source-dirs:- test- default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DerivingVia EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances UndecidableSuperClasses 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 -threaded -eventlog -rtsopts "-with-rtsopts=-N -A64m -AL256m"- build-tool-depends:- tasty-discover:tasty-discover- build-depends:- HUnit- , aeson+ aeson , base >=4.7 && <5 , bytestring- , containers- , data-default- , directory- , filepath , fmt- , formatting- , hedgehog- , hex-text- , hspec- , hspec-expectations- , lens+ , haskeline , megaparsec >=7.0.0 , morley , morley-prelude- , singletons- , syb- , tasty- , tasty-hedgehog- , tasty-hspec- , tasty-hunit-compat+ , named+ , optparse-applicative+ , pretty-simple , text- , unordered-containers- mixins:- base hiding (Prelude)- 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: AllowAmbiguousTypes ApplicativeDo BangPatterns BlockArguments ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DerivingVia EmptyCase FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NegativeLiterals NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms PolyKinds QuasiQuotes RankNTypes RecordWildCards RecursiveDo ScopedTypeVariables StandaloneDeriving StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeOperators UndecidableInstances UndecidableSuperClasses 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 >=4.7 && <5- , containers- , gauge- , megaparsec >=7.0.0- , morley- , morley-prelude+ , vinyl mixins: base hiding (Prelude) default-language: Haskell2010
src/Michelson/Doc.hs view
@@ -52,7 +52,6 @@ import Data.Typeable (cast, typeRep) import Development.GitRev (gitCommitDate, gitHash) import Fmt (Buildable, build, fmt, (+|), (+||), (|+), (||+))-import GHC.TypeNats (Nat) import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Lift as TH import System.Environment (lookupEnv)@@ -82,16 +81,17 @@ -- E.g. all rendering and reording stuff could be merged in one method, and -- we could have several template implementations for it which would allow -- user to specify only stuff relevant to his case.-class (Typeable d, DOrd d, KnownNat (DocItemPosition d)) => DocItem d where+class (Typeable d, DOrd d) => DocItem d where -- | Position of this item in the resulting documentation; -- the smaller the value, the higher the section with this element- -- will be placed.+ -- will be placed. If the position is the same as other doc items,+ -- they will be placed base on their name, alphabetically. -- -- Documentation structure is not necessarily flat. -- If some doc item consolidates a whole documentation block within it, -- this block will have its own placement of items independent from outer parts -- of the doc.- type DocItemPosition d = (pos :: Nat) | pos -> d+ docItemPos :: Natural -- | When multiple items of the same type belong to one section, how -- this section will be called.@@ -165,7 +165,7 @@ -- | Get doc item position at term-level. docItemPosition :: forall d. DocItem d => DocItemPos-docItemPosition = DocItemPos $ natVal (Proxy @(DocItemPosition d))+docItemPosition = DocItemPos (docItemPos @d, show (typeRep $ Proxy @d)) -- | Render an item into Markdown block with all required adjustments. docItemToMarkdownFull :: DocItem d => HeaderLevel -> d -> Markdown@@ -200,10 +200,12 @@ deriving newtype (ToAnchor) -- | Position of all doc items of some type.-newtype DocItemPos = DocItemPos Natural+newtype DocItemPos = DocItemPos (Natural, Text) deriving stock (Eq, Ord, Show)- deriving newtype (Buildable) +instance Buildable DocItemPos where+ build (DocItemPos (a, _)) = build a+ -- | Where do we place given doc item. data DocItemPlacementKind = DocItemInlined@@ -473,7 +475,7 @@ newtype DGeneralInfoSection = DGeneralInfoSection SubDoc instance DocItem DGeneralInfoSection where- type DocItemPosition DGeneralInfoSection = 1+ docItemPos = 1 docItemSectionName = Nothing docItemToMarkdown lvl (DGeneralInfoSection subDoc) = subDocToMarkdown lvl subDoc@@ -482,7 +484,7 @@ data DName = DName Text SubDoc instance DocItem DName where- type DocItemPosition DName = 3+ docItemPos = 3 docItemSectionName = Nothing docItemToMarkdown lvl (DName name doc) = mdHeader lvl (build name) <>@@ -492,7 +494,7 @@ data DDescription = DDescription Markdown instance DocItem DDescription where- type DocItemPosition DDescription = 10+ docItemPos = 10 docItemSectionName = Nothing docItemToMarkdown _ (DDescription txt) = build txt @@ -553,7 +555,7 @@ value -> error $ "Unknown value returned by git: " <> show value instance DocItem DGitRevision where- type DocItemPosition DGitRevision = 2+ docItemPos = 2 docItemSectionName = Nothing docItemToMarkdown _ (DGitRevisionKnown DGitRevisionInfo{..}) = mconcat@@ -571,7 +573,7 @@ data DComment = DComment Text instance DocItem DComment where- type DocItemPosition DComment = 0+ docItemPos = 0 docItemSectionName = Nothing docItemToMarkdown _ (DComment commentText) = "<!---\n" +| commentText |+ "\n-->"@@ -580,6 +582,6 @@ data DAnchor = DAnchor Anchor instance DocItem DAnchor where- type DocItemPosition DAnchor = 4+ docItemPos = 4 docItemSectionName = Nothing docItemToMarkdown _ (DAnchor a) = mdAnchor a
− src/Michelson/Doc/Test.hs
@@ -1,243 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Utilities for testing documentations sanity.------ These tests serve to ensure that documentation generation is not broken and--- that user follows sane documentation structure (e.g. contract should be--- named, some entities require description, e.t.c).-module Michelson.Doc.Test- ( DocTest (..)- , mkDocTest- , excludeDocTest- , excludeDocTests- , runDocTests- , expectDocTestFailure-- -- * Test predicates- , testDocBasic-- -- ** Individual test predicates- , testContractNameAtTop- , testNoGitInfo- , testDocNotEmpty- , testNoAdjacentDescriptions- , testStorageIsDocumented-- -- * Utilities- , forEachContractLayer- , 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)-import GHC.Stack (SrcLoc)-import Test.HUnit (Assertion, assertBool, assertFailure)-import Test.HUnit.Lang (HUnitFailure(..))-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)-import qualified Text.Show--import Michelson.Doc-import Michelson.Typed.Haskell.Doc--------------------------------------------------------------------------------- Framework--------------------------------------------------------------------------------- | Test case for contract documentation.-data DocTest = DocTest- { dtDeclLoc :: SrcLoc- -- ^ Declaration location, used to distinguish different test predicates.- -- This is primarily used in 'excludeDocTest'.- , dtDesc :: String- -- ^ Description of predicate, which you put to 'testCase'.- , dtSuite :: HasCallStack => ContractDoc -> Assertion- -- ^ Test itself.- }--instance Eq DocTest where- (==) = (==) `on` dtDeclLoc--instance Show DocTest where- show = pretty--instance Buildable DocTest where- 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- -> (HasCallStack => ContractDoc -> Assertion)- -> DocTest-mkDocTest dtDesc dtSuite = DocTest{..}- where- (_, dtDeclLoc) = case getCallStack callStack of- [] -> error "Callstacks operate in a weird way, excluding doc tests won't work"- layer : _ -> layer---- | Exclude given test suite.-excludeDocTest :: HasCallStack => DocTest -> [DocTest] -> [DocTest]-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@.-excludeDocTests :: [DocTest] -> [DocTest] -> [DocTest]-excludeDocTests = foldr excludeDocTest---- | Finalize test suites.-runDocTests :: HasCallStack => [DocTest] -> ContractDoc -> [TestTree]-runDocTests tests contract =- tests <&> \DocTest{..} ->- testCase dtDesc (dtSuite contract)---- | Ensure that 'DocTest' check fires on given contract.--- Used in tests on this module.-expectDocTestFailure :: DocTest -> ContractDoc -> Assertion-expectDocTestFailure DocTest{..} contract = do- passed <- (dtSuite contract $> True) `catch` \HUnitFailure{} -> pure False- when passed $ assertFailure "Test didn't fail unexpectedly"--------------------------------------------------------------------------------- Test predicates--------------------------------------------------------------------------------- Utilities--------------------------------------------------------------------------------- | Apply given function to each group (created with 'DocGroup' instruction)--- recursively.--- This function will accept grouping doc item itself and its subcontents.-forEachLayer :: DocBlock -> (SomeDocItem -> DocBlock -> r) -> [r]-forEachLayer block f = do- DocSection docElems <- toList block- DocElem{..} <- toList docElems- Just (SubDoc sub) <- pure deSub- f (SomeDocItem deItem) sub : forEachLayer sub f---- | Apply given function to each group (created with 'DocGroup' instruction)--- within a contract recursively.--- This function will accept grouping doc item itself (unless we are at root)--- and its subcontents.-forEachContractLayer :: ContractDoc -> (Maybe SomeDocItem -> DocBlock -> r) -> [r]-forEachContractLayer contract f =- let contents = cdContents contract- in f Nothing contents- : forEachLayer contents (\sdi blk -> f (Just sdi) blk)---- | Apply given function to each doc item within a contract.-forEachContractDocItem :: DocItem d => ContractDoc -> (d -> r) -> [r]-forEachContractDocItem contract f =- fold . forEachContractLayer contract $ \_ block ->- fmap f . maybe [] toList $ lookupDocBlockSection block---- Basic predicates--------------------------------------------------------------------------------- | Check that contract documentation is wrapped with 'contractName'.-testContractNameAtTop :: DocTest-testContractNameAtTop =- mkDocTest "The whole contract is wrapped into 'DName'" $- \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--- most of the documentation pieces being lost.-testDocNotEmpty :: DocTest-testDocNotEmpty =- mkDocTest "There is at least one DOC_ITEM" $- \contractDoc ->- assertBool "No doc items found" . or $- forEachContractLayer contractDoc hasDocItem- where- hasDocItem _ block = not . null @[()] $ do- DocSection docElems <- toList block- docElem <- toList docElems- guard (deIsAtomic docElem)---- | Check that no group contains two 'DDescription' items.------ Normally such behaviour is allowed and can be exploited, but often it is not--- and multiple descriptions appearence under the same group signals about--- missing grouping wrapper (e.g. use of 'caseT' instead of 'entryCase').-testNoAdjacentDescriptions :: DocTest-testNoAdjacentDescriptions =- mkDocTest "No two 'DDescription' appear under the same group" $- \contractDoc ->- sequence_ . forEachContractLayer contractDoc $ \_ block ->- case lookupDocBlockSection @DDescription block of- Just ds@(_ :| _ : _) ->- let txts = ds <&> \(DDescription txt) -> txt- in assertFailure . fmt $- nameF "Found multiple adjacent descriptions" $- blockListF $ map quotes txts- _ -> pass- where- quotes t = "\"" <> t <> "\""---- | Check that all descriptions are proper.-testDescriptionsAreWellFormatted :: DocTest-testDescriptionsAreWellFormatted =- mkDocTest "Descriptions are well-formatted" $- \contractDoc ->- sequence_ . forEachContractDocItem contractDoc $ \(DDescription desc) ->- check (toText $ toLazyText desc)- where- check desc- | T.null desc =- assertFailure "Empty description."- | T.last (T.stripEnd desc) /= '.' =- assertFailure $ "Description does not end with a dot:\n\"" <>- toString desc <> "\""- | otherwise = pass---- | Test whether storage documentation is included in the contract's documentation.-testStorageIsDocumented :: DocTest-testStorageIsDocumented =- mkDocTest "Storage documentation is included" $- \contractDoc -> assertBool "No documentation for storage in the contract." $- or $ forEachContractDocItem contractDoc $ \(DStorageType _) -> True---- | Base properties which should comply for all documentations.-testDocBasic :: [DocTest]-testDocBasic =- [ testContractNameAtTop- , testNoGitInfo- , testDocNotEmpty- , testNoAdjacentDescriptions- , testDescriptionsAreWellFormatted- , testStorageIsDocumented- ]
src/Michelson/Interpret.hs view
@@ -40,15 +40,15 @@ import Data.Default (Default(..)) import qualified Data.Map as Map import qualified Data.Set as Set-import Data.Singletons (Sing)+import Data.Singletons (Sing, demote) import Data.Vinyl (Rec(..), (<+>))-import Fmt (Buildable(build), Builder, genericF, pretty)+import Fmt (Buildable(build), Builder, genericF) import Michelson.Interpret.Pack (packValue') import Michelson.Interpret.Unpack (UnpackError, unpackValue') import Michelson.TypeCheck- (SomeContract(..), TCError, TcOriginatedContracts, matchTypes, runTypeCheck, typeCheckContract,- typeCheckValue)+ (SomeContract(..), SomeParamType(..), TCError, TcOriginatedContracts, TypeCheckMode(..),+ matchTypes, runTypeCheck, typeCheckContract, typeCheckValue) import Michelson.Typed import qualified Michelson.Typed as T import qualified Michelson.Untyped as U@@ -187,17 +187,17 @@ -> ContractEnv -> Either InterpretError InterpretResult interpretUntyped uContract@U.Contract{..} paramU initStU env = do- SomeContract (Contract (instr :: ContractCode cp st) _ _)- <- first IllTypedContract $ typeCheckContract (ceContracts env) uContract+ SomeContract (Contract (instr :: ContractCode cp st) _ _ _)+ <- first IllTypedContract $ typeCheckContract uContract -- Do creates dummy scope to somehow overcome this: -- GHC internal error: ‘st’ is not in scope during type checking, but it passed the renamer. do let runTC :: forall t. SingI t => U.Value -> Either TCError (Value t)- runTC =- runTypeCheck contractParameter (ceContracts env) .+ runTC val =+ runTypeCheck (TypeCheckValue (val, demote @t)) . usingReaderT def .- typeCheckValue @t+ typeCheckValue @t $ val paramV <- first IllTypedParam $ runTC @cp paramU initStV <- first IllTypedStorage $ runTC @st initStU@@ -216,7 +216,7 @@ interpret' :: forall cp st arg. ContractCode cp st- -> EntryPointCallT cp arg+ -> EntrypointCallT cp arg -> T.Value arg -> T.Value st -> ContractEnv@@ -237,7 +237,7 @@ interpret :: ContractCode cp st- -> EntryPointCallT cp arg+ -> EntrypointCallT cp arg -> T.Value arg -> T.Value st -> ContractEnv@@ -503,15 +503,8 @@ castContract addr epName T.tyImplicitAccountParam :& r ContractAddress ca -> case Map.lookup ca ceContracts of- -- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has- -- valid parameter type. Should be not necessary after [#36].- Just tc@(U.ParameterType (AsUTypeExt (_ :: Sing tc) tcNotes) rootAnn) ->- let paramNotes = ParamNotesUnsafe tcNotes rootAnn in- case T.checkScope @(T.ParameterScope tc) of- Right Dict -> castContract addr epName paramNotes :& r- _ -> error $ "Illegal type in parameter of env contract: " <> pretty tc- -- TODO [#36]: we can do this safely once 'TcOriginatedContracts' stores- -- typed stuff.+ Just (SomeParamType _ paramNotes) ->+ castContract addr epName paramNotes :& r Nothing -> VOption Nothing :& r where castContract@@ -519,7 +512,7 @@ => 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+ MkEntrypointCallRes na epc <- T.mkEntrypointCall epName param Right (Refl, _) <- pure $ matchTypes nt na return $ VContract addr (T.SomeEpc epc)
src/Michelson/Interpret/Pack.hs view
@@ -351,15 +351,12 @@ "\x03\x4d" SET_DELEGATE -> "\x03\x4e"- CREATE_CONTRACT (Contract instr np ng)- | _ :: Instr '[ 'TPair p g ] '[ 'TPair ('TList 'TOperation) g ] <- instr ->- let contents =- [ "\x05\x00" <> encodeParamNotes' @p np- , "\x05\x01" <> encodeNotedT' @g ng- , "\x05\x02" <> encodeInstrs instr- ]- -- TODO [TM-96] These ^ should be encoded in the same order in which- -- they appear in the original code+ CREATE_CONTRACT contract@Contract{..}+ | _ :: Instr '[ 'TPair p g ] '[ 'TPair ('TList 'TOperation) g ] <- cCode ->+ let contents = mapEntriesOrdered contract+ (\np -> "\x05\x00" <> encodeParamNotes' @p np)+ (\ng -> "\x05\x01" <> encodeNotedT' @g ng)+ (\instr -> "\x05\x02" <> encodeInstrs instr) in "\x05\x1d" <> encodeList id contents IMPLICIT_ACCOUNT -> "\x03\x1e"
src/Michelson/Interpret/Unpack.hs view
@@ -35,35 +35,28 @@ import Data.Constraint (Dict(..)) import Data.Default (def) import qualified Data.Kind as Kind-import qualified Data.List as List import qualified Data.Map as Map import qualified Data.Set as Set import Data.Singletons (Sing, SingI(..)) import Data.Typeable ((:~:)(..))-import Fmt (Buildable, build, fmt, hexF, pretty, (+|), (|+))+import Fmt (Buildable, fmt, hexF, pretty, (+|), (|+)) import Michelson.Parser (Parser, ParserException(..), parseNoEnv) import qualified Michelson.Parser.Annotations as PA import Michelson.Text import Michelson.TypeCheck (HST(..), SomeHST(..), SomeInstr(..), SomeInstrOut(..), TCError(..), TypeCheckEnv(..),- TypeContext(..), withWTPm)+ TypeCheckMode(..), TypeContext(..), withWTPm) import Michelson.TypeCheck.Helpers (ensureDistinctAsc, eqHST1) import Michelson.TypeCheck.Instr (typeCheckList) import Michelson.Typed (KnownT, RemFail(..), SingT(..), starNotes) import qualified Michelson.Typed as T-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Scope (UnpackedValScope) import Michelson.Untyped-import Tezos.Address (Address(..), ContractHash(..)) import Tezos.Core import Tezos.Crypto- (KeyHash(..), KeyHashTag(..), PublicKey(..), keyHashLengthBytes, mkSignature, parseKeyHash,- parsePublicKey, parseSignature)-import qualified Tezos.Crypto.Ed25519 as Ed25519-import qualified Tezos.Crypto.P256 as P256-import qualified Tezos.Crypto.Secp256k1 as Secp256k1-import Util.Binary (UnpackError (..), ensureEnd, launchGet)+import Util.Binary ---------------------------------------------------------------------------- -- Helpers@@ -74,26 +67,6 @@ (?) = flip Get.label infix 0 ? --- | Get a bytestring of the given length leaving no references to the--- original data in serialized form.-getByteStringCopy :: Int -> Get ByteString-getByteStringCopy = fmap BS.copy . Get.getByteString---- | Get remaining available bytes.------ Note that reading all remaining decoded input may be expensive and is thus--- discouraged, use can use this function only when you know that amount--- of data to be consumed is limited, e.g. within 'decodeAsBytes' call.-getRemainingByteStringCopy :: Get ByteString-getRemainingByteStringCopy = do- lbs <- Get.getRemainingLazyByteString- -- Avoiding memory overflows in case bad length to 'Get.isolate' was provided.- -- Normally this function is used only to decode primitives, 'Signature' in- -- the worst case, so we could set little length, but since this is a hack- -- anyway let's make sure it never obstructs our work.- when (length lbs > 640000) $ fail "Too big length for an entity"- return (LBS.toStrict lbs)- -- | Read a byte and match it against given value. expectTag :: String -> Word8 -> Get () expectTag desc t =@@ -103,10 +76,6 @@ fail . fmt $ "Unexpected tag value (expected 0x" +| hexF t |+ ", but got 0x" +| hexF t' |+ ")" --- | Fail with "unknown tag" error.-unknownTag :: String -> Word8 -> Get a-unknownTag desc tag =- fail . fmt $ "Unknown " <> build desc <> " tag: 0x" <> hexF tag -- | Read a byte describing the primitive going further and match it against -- expected tag in the given conditions.@@ -143,29 +112,6 @@ then return [] else (:) <$> decode <*> manyForced decode --- | Describes how 'decodeWithTag' should decode tag-dependent data.--- We expect bytes of such structure: 'tdTag' followed by a bytestring--- which will be parsed with 'tdDecoder'.-data TaggedDecoder a = TaggedDecoder- { tdTag :: Word8- , tdDecoder :: Get a- }---- | Alias for 'TaggedDecoder' constructor.-(#:) :: Word8 -> Get a -> TaggedDecoder a-(#:) = TaggedDecoder-infixr 0 #:---- Common decoder for the case when packed data starts with a tag (1--- byte) that specifies how to decode remaining data.-decodeWithTag :: String -> [TaggedDecoder a] -> Get a-decodeWithTag what decoders = Get.label what $ do- tag <- Get.label (what <> " tag") Get.getWord8- -- Number of decoders is usually small, so linear runtime lookup should be ok.- case List.find ((tag ==) . tdTag) decoders of- Nothing -> unknownTag what tag- Just TaggedDecoder{..} -> tdDecoder- ---------------------------------------------------------------------------- -- Michelson serialisation ----------------------------------------------------------------------------@@ -219,14 +165,7 @@ decodeValue = Get.label "Value" $ case sing @t of STKey -> T.VKey <$> decodeAsBytesOrString- ( decodeWithTag "key"- [ 0x00 #: decodeBytesLike "key Ed25519"- (fmap PublicKeyEd25519 . Ed25519.mkPublicKey)- , 0x01 #: decodeBytesLike "key Secp256k1"- (fmap PublicKeySecp256k1 . Secp256k1.mkPublicKey)- , 0x02 #: decodeBytesLike "key P256"- (fmap PublicKeyP256 . P256.mkPublicKey)- ]+ ( decodeWithTag "key" keyDecoders , parsePublicKey ) STUnit -> do@@ -303,7 +242,10 @@ expectTag "Timestamp" 0x00 T.VTimestamp . timestampFromSeconds <$> decodeInt STAddress ->- T.VAddress <$> decodeEpAddress+ T.VAddress <$> decodeAsBytesOrString+ ( decodeBytesLike "EpAddress" parseEpAddressRaw+ , parseEpAddress+ ) withUnpackedValueScope :: forall a v m. (KnownT a, MonadFail m)@@ -322,15 +264,6 @@ Just Dict -> act Nothing -> fail "Comparable type is required here" -keyHashDecoders :: [TaggedDecoder KeyHash]-keyHashDecoders =- [ 0x00 #: KeyHash KeyHashEd25519 <$> getPayload- , 0x01 #: KeyHash KeyHashSecp256k1 <$> getPayload- , 0x02 #: KeyHash KeyHashP256 <$> getPayload- ]- where- getPayload = getByteStringCopy keyHashLengthBytes- -- | Read length of something (list, string, ...). decodeLength :: Get Int decodeLength = Get.label "Length" $ do@@ -384,14 +317,6 @@ Get.isolate l bytesDecoder ? "Binary content" other -> unknownTag "text or string" other -decodeBytesLike- :: (Buildable err)- => String -> (ByteString -> Either err a) -> Get a-decodeBytesLike what constructor = do- bs <- getRemainingByteStringCopy- case constructor bs of- Left err -> fail $ "Wrong " +| what |+ ": " +| err |+ ""- Right res -> pure res decodeBytesLikeMaybe :: String -> (ByteString -> Maybe a) -> Get a@@ -417,33 +342,6 @@ either (fail . toString) pure $ Map.fromDistinctAscList <$> ensureDistinctAsc fst es -decodeEpAddress :: Get EpAddress-decodeEpAddress = Get.label "Address (maybe with entrypoint)" $- decodeAsBytesOrString- ( decodeWithTag "address"- [ 0x00 #: Get.label "Plain address" $ do- eaAddress <- KeyAddress <$>- decodeWithTag "key_hash inside address" keyHashDecoders- eaEntryPoint <- decodeEpName- return EpAddress{..}- , 0x01 #: Get.label "Contract address" $ do- eaAddress <- ContractAddress . ContractHash <$>- getByteStringCopy keyHashLengthBytes- expectTag "Contract address suffix" 0x00- eaEntryPoint <- decodeEpName- return EpAddress{..}- ]- , parseEpAddress- )--decodeEpName :: Get EpName-decodeEpName = do- ss <- many Get.getWord8 ? "EpName' String content"- s <- decodeUtf8' (BS.pack ss)- & either (fail . show) pure- ? "EpName' String UTF-8 decoding"- either (fail . pretty) pure $ epNameFromRefAnn (ann s)- -- | Read a numeric value. decodeInt :: Num i => Get i decodeInt = fromIntegral @Integer <$> loop 0 0 ? "Number"@@ -500,12 +398,7 @@ TypeCheckEnv { tcExtFrames = error "runInstrImpl(UNPACK): tcExtFrames touched" --- ^ This is safe because @UNPACK@ never produces Ext instructions- , tcContractParam = error "runInstrImpl(UNPACK): tcContractParam touched"- --- ^ Used only in @SELF@ interpretation,- --- but there is no way for @SELF@ to appear in packed data- , tcContracts = error "runInstrImpl(UNPACK): tcContracts touched"- --- ^ Used only in typechecking of @contract@ values,- --- but it's not possible to unpack to ones.+ , tcMode = TypeCheckPack } decodeInstr :: Get ExpandedInstr@@ -722,17 +615,36 @@ decodeContract :: Get Contract decodeContract = decodeAsList $ do- expectTag "Pre contract parameter" 0x05- expectTag "Contract parameter" 0x00- (t, ta, root) <- decodeTWithAnns- expectTag "Pre contract storage" 0x05- expectTag "Contract storage" 0x01- s <- decodeType- expectTag "Pre contract code" 0x05- expectTag "Contract code" 0x02- c <- decodeOps- pure (Contract (ParameterType (Type t ta) (convAnn root)) s c)+ result <- contractTuple+ case orderContractBlock result of+ Just contract' -> do+ pure contract'+ Nothing ->+ fail "Duplicate contract field."+ where+ decodeParamsBlock = CBParam <$> do+ expectTag "Pre contract parameter" 0x05+ expectTag "Contract parameter" 0x00+ (t, ta, root) <- decodeTWithAnns+ pure $ ParameterType (Type t ta) (convAnn root)+ decodeStorageBlock = CBStorage <$> do+ expectTag "Pre contract storage" 0x05+ expectTag "Contract storage" 0x01+ decodeType+ decodeCodeBlock = CBCode <$> do+ expectTag "Pre contract code" 0x05+ expectTag "Contract code" 0x02+ decodeOps + contractBlock = decodeParamsBlock <|> decodeStorageBlock <|> decodeCodeBlock++ contractTuple = do+ result1 <- contractBlock+ result2 <- contractBlock+ result3 <- contractBlock+ pure (result1, result2, result3)++ decodeOp :: Get ExpandedOp decodeOp = Get.label "Op" $ do tag <- Get.lookAhead Get.getWord8@@ -779,6 +691,7 @@ 0x05 -> decodeWithTFAnns (ct,,) _ -> fail failMessage +{-# ANN decodeTWithAnns ("HLint: ignore Redundant <$>" :: Text) #-} decodeTWithAnns :: Get (T, TypeAnn, FieldAnn) decodeTWithAnns = doDecode <|> decodeTc ? "Type" where
src/Michelson/Macro.hs view
@@ -7,6 +7,7 @@ -- * Macros types CadrStruct (..) , PairStruct (..)+ , UnpairStruct (..) , Macro (..) , LetMacro (..) @@ -23,7 +24,8 @@ , expandValue -- * For parsing- , mapLeaves+ , mapPairLeaves+ , mapUnpairLeaves -- * Internals exported for tests , expand@@ -62,7 +64,7 @@ instance NFData LetMacro data PairStruct- = F (VarAnn, FieldAnn)+ = F FieldAnn | P PairStruct PairStruct deriving stock (Eq, Show, Data, Generic) @@ -71,6 +73,16 @@ instance Buildable PairStruct where build = genericF +data UnpairStruct+ = UF (VarAnn, FieldAnn)+ | UP UnpairStruct UnpairStruct+ deriving stock (Eq, Show, Data, Generic)++instance NFData UnpairStruct++instance Buildable UnpairStruct where+ build = genericF+ data CadrStruct = A | D@@ -127,7 +139,7 @@ | IFCMP ParsedInstr VarAnn [ParsedOp] [ParsedOp] | FAIL | PAPAIR PairStruct TypeAnn VarAnn- | UNPAIR PairStruct+ | UNPAIR UnpairStruct | CADR [CadrStruct] VarAnn FieldAnn | SET_CADR [CadrStruct] VarAnn FieldAnn | MAP_CADR [CadrStruct] VarAnn FieldAnn [ParsedOp]@@ -208,7 +220,7 @@ -- It's needed because we have a hack in parser: we parse PAIR as PAPAIR. -- We need to do something better eventually. (Mac (PAPAIR (P (F a) (F b)) t v) pos) ->- WithSrcEx (ics pos) $ PrimEx (PAIR t v (snd a) (snd b))+ WithSrcEx (ics pos) $ PrimEx (PAIR t v a b) -- DIIP is now always represented as a single instruction. -- `expandMacro` always returns a list which we wrap into `SeqEx`, so we -- can't use it.@@ -229,7 +241,7 @@ expandMacro :: InstrCallStack -> Macro -> [ExpandedOp] expandMacro p@InstrCallStack{icsCallStack=cs,icsSrcPos=macroPos} = \case- VIEW a -> expandMacro p (UNPAIR $ P (F (noAnn,noAnn)) (F (noAnn,noAnn))) +++ VIEW a -> expandMacro p (UNPAIR $ UP (UF (noAnn, noAnn)) (UF (noAnn, noAnn))) ++ [ PrimEx (DIP $ expandMacro p $ DUUP 2 noAnn) ] ++ [ PrimEx $ PAIR noAnn noAnn noAnn noAnn ] ++ (expand cs <$> a) ++@@ -240,7 +252,7 @@ , PrimEx $ CONS noAnn , PrimEx $ PAIR noAnn noAnn noAnn noAnn ]- VOID a -> expandMacro p (UNPAIR (P (F (noAnn,noAnn)) (F (noAnn,noAnn)))) +++ VOID a -> expandMacro p (UNPAIR (UP (UF (noAnn, noAnn)) (UF (noAnn, noAnn)))) ++ [ PrimEx SWAP , PrimEx $ DIP $ expand cs <$> a , PrimEx SWAP@@ -285,11 +297,11 @@ -- expansions are not explicitly documented in the Michelson Specification expandPapair :: InstrCallStack -> PairStruct -> TypeAnn -> VarAnn -> [ExpandedOp] expandPapair ics ps t v = case ps of- P (F a) (F b) -> [PrimEx $ PAIR t v (snd a) (snd b)]+ P (F a) (F b) -> [PrimEx $ PAIR t v a b] P (F a) r -> PrimEx <$> [ DIP $ expandMacro ics (PAPAIR r noAnn noAnn)- , PAIR t v (snd a) noAnn]+ , PAIR t v a noAnn] P l (F b) -> expandMacro ics (PAPAIR l noAnn noAnn) ++- [PrimEx $ PAIR t v noAnn (snd b)]+ [PrimEx $ PAIR t v noAnn b] P l r -> expandMacro ics (PAPAIR l noAnn noAnn) ++ [ PrimEx $ DIP $ expandMacro ics (PAPAIR r noAnn noAnn) , PrimEx $ PAIR t v noAnn noAnn]@@ -297,34 +309,34 @@ -- It's impossible from the structure of PairStruct and considered cases above, -- but if it accidentally happened let's just do nothing. -expandUnpapair :: InstrCallStack -> PairStruct -> [ExpandedOp]+expandUnpapair :: InstrCallStack -> UnpairStruct -> [ExpandedOp] expandUnpapair ics = \case- P (F (v,f)) (F (w,g)) ->+ UP (UF (v,f)) (UF (w,g)) -> PrimEx <$> [ DUP noAnn , CAR v f , DIP [PrimEx $ CDR w g] ]- P (F (v, f)) r ->+ UP (UF (v, f)) r -> PrimEx <$> [ DUP noAnn , CAR v f , DIP (PrimEx (CDR noAnn noAnn) : expandMacro ics (UNPAIR r)) ]- P l (F (v, f)) ->+ UP l (UF (v, f)) -> map PrimEx [ DUP noAnn , DIP [PrimEx $ CDR v f] , CAR noAnn noAnn ] ++ expandMacro ics (UNPAIR l)- P l r ->+ UP l r -> expandMacro ics unpairOne ++ [PrimEx $ DIP $ expandMacro ics $ UNPAIR r] ++ expandMacro ics (UNPAIR l)- F _ -> [] -- Do nothing in this case.- -- It's impossible from the structure of PairStruct and considered cases above,+ UF _ -> [] -- Do nothing in this case.+ -- It's impossible from the structure of UnpairStruct and considered cases above, -- but if it accidentally happened let's just do nothing. where- unpairOne = UNPAIR (P fn fn)- fn = F (noAnn, noAnn)+ unpairOne = UNPAIR (UP fn fn)+ fn = UF (noAnn, noAnn) expandCadr :: InstrCallStack -> [CadrStruct] -> VarAnn -> FieldAnn -> [ExpandedOp] expandCadr ics cs v f = case cs of@@ -420,25 +432,41 @@ ] pre e = Endo (e :) --mapLeaves :: [(VarAnn, FieldAnn)] -> PairStruct -> PairStruct-mapLeaves fs p = evalState (leavesST p) fs+mapPairLeaves :: [FieldAnn] -> PairStruct -> PairStruct+mapPairLeaves fs p = evalState (pairLeavesST p) fs -leavesST :: PairStruct -> State [(VarAnn, FieldAnn)] PairStruct-leavesST = \case+pairLeavesST :: PairStruct -> State [FieldAnn] PairStruct+pairLeavesST = \case (P l r) -> do- l' <- leavesST l- r' <- leavesST r+ l' <- pairLeavesST l+ r' <- pairLeavesST r return $ P l' r' (F _) -> do f <- state getLeaf return $ F f where getLeaf (a:as) = (a, as)+ getLeaf _ = (noAnn, [])++mapUnpairLeaves :: [(VarAnn, FieldAnn)] -> UnpairStruct -> UnpairStruct+mapUnpairLeaves fs p = evalState (unpairLeavesST p) fs++unpairLeavesST :: UnpairStruct -> State [(VarAnn, FieldAnn)] UnpairStruct+unpairLeavesST = \case+ (UP l r) -> do+ l' <- unpairLeavesST l+ r' <- unpairLeavesST r+ return $ UP l' r'+ (UF _) -> do+ f <- state getLeaf+ return $ UF f+ where+ getLeaf (a:as) = (a, as) getLeaf _ = ((noAnn, noAnn), []) deriveJSON morleyAesonOptions ''ParsedOp deriveJSON morleyAesonOptions ''LetMacro deriveJSON morleyAesonOptions ''PairStruct+deriveJSON morleyAesonOptions ''UnpairStruct deriveJSON morleyAesonOptions ''CadrStruct deriveJSON morleyAesonOptions ''Macro
src/Michelson/OpSize.hs view
@@ -155,7 +155,7 @@ expandedInstrsOpSize = foldMap expandedInstrOpSize contractOpSize :: Contract -> OpSize-contractOpSize (Contract (ParameterType cp rootAnn) st is) =+contractOpSize (Contract (ParameterType cp rootAnn) st is _) = OpSize 16 <> typeOpSize' [convAnn rootAnn] cp <> typeOpSize st <> expandedInstrsOpSize is numOpSize :: Integral i => i -> OpSize
src/Michelson/Parser.hs view
@@ -23,6 +23,7 @@ -- * For tests , codeEntry+ , ops , type_ , letInner , letType@@ -41,7 +42,6 @@ import Prelude hiding (try) -import Control.Applicative.Permutations (intercalateEffect, toPermutation) import qualified Language.Haskell.TH.Quote as TH import Text.Megaparsec (Parsec, choice, eitherP, eof, errorBundlePretty, getSourcePos, lookAhead, parse, try)@@ -90,27 +90,66 @@ env <- fromMaybe noLetEnv <$> (optional (letBlock parsedOp)) local (const env) contract +cbParameter :: Parser ParameterType+cbParameter = do+ (fieldAnn, t) <- symbol "parameter" *> field+ pure $ ParameterType t (convAnn fieldAnn)++cbStorage :: Parser Type+cbStorage = symbol "storage" *> type_++cbCode :: Parser [ParsedOp]+cbCode = symbol "code" *> codeEntry++contractBlock :: Parser (ContractBlock ParsedOp)+contractBlock = choice+ [ (CBParam <$> cbParameter)+ , (CBStorage <$> cbStorage)+ , (CBCode <$> cbCode)+ ]++-- | This ensures that the error message will point to the correct line.+ensureNotDuplicate :: [ContractBlock ParsedOp] -> ContractBlock ParsedOp -> Parser ()+ensureNotDuplicate blocks result =+ let+ failDuplicateField a = fail $ "Duplicate contract field: " <> a+ in+ case (result, blocks) of+ (CBParam _, CBParam _ : _) -> failDuplicateField "parameter"+ (CBStorage _, CBStorage _: _) -> failDuplicateField "storage"+ (CBCode _, CBCode _: _) -> failDuplicateField "code"+ (_, _:xs) -> ensureNotDuplicate xs result+ (_, []) -> pure ()+ -- | Michelson contract contract :: Parser (Contract' ParsedOp) contract = do mSpace- ((r, p), s, c) <- braces contractTuple <|> contractTuple- return $ Contract (ParameterType p (convAnn r)) s c+ result <- braces contractTuple <|> contractTuple+ case orderContractBlock result of+ Just contract' ->+ return contract'+ Nothing ->+ fail $ "Duplicate contract field: " <> show result where- contractTuple = intercalateEffect semicolon $- (,,) <$> toPermutation parameter- <*> toPermutation storage- <*> toPermutation code-- parameter :: Parser (FieldAnn, Type)- parameter = symbol "parameter" *> field-- storage :: Parser Type- storage = symbol "storage" *> type_+ -- | @ensureNotDuplicate@ provides a better message and point to the correct line+ -- when the parser fails.+ contractTuple = do+ result1 <- contractBlock+ semicolon - code :: Parser [ParsedOp]- code = symbol "code" *> codeEntry+ result2 <- do+ r <- contractBlock+ ensureNotDuplicate [result1] r+ pure r+ semicolon + result3 <- do+ r <- contractBlock+ ensureNotDuplicate [result1, result2] r+ pure r+ optional semicolon+ pure (result1, result2, result3) -- Value ------------------
src/Michelson/Parser/Macro.hs view
@@ -19,7 +19,7 @@ import Text.Megaparsec (customFailure, notFollowedBy, try) import Text.Megaparsec.Char.Lexer (decimal) -import Michelson.Macro (CadrStruct(..), Macro(..), PairStruct(..), ParsedOp(..))+import Michelson.Macro (CadrStruct(..), Macro(..), PairStruct(..), UnpairStruct(..), ParsedOp(..)) import qualified Michelson.Macro as Macro import Michelson.Parser.Annotations import Michelson.Parser.Error@@ -68,28 +68,35 @@ where num str = fromIntegral . length <$> some (string' str) +pairMacInner :: Parser PairStruct+pairMacInner = do+ string' "P"+ l <- (string' "A" $> F noAnn) <|> pairMacInner+ r <- (string' "I" $> F noAnn) <|> pairMacInner+ return $ P l r+ pairMac :: Parser Macro pairMac = do a <- pairMacInner symbol' "R" (tn, vn, fns) <- permute3Def noteDef note (some note)- let ps = Macro.mapLeaves ((noAnn,) <$> fns) a+ let ps = Macro.mapPairLeaves fns a return $ PAPAIR ps tn vn -pairMacInner :: Parser PairStruct-pairMacInner = do+upairMacInner :: Parser UnpairStruct+upairMacInner = do string' "P"- l <- (string' "A" $> F (noAnn, noAnn)) <|> pairMacInner- r <- (string' "I" $> F (noAnn, noAnn)) <|> pairMacInner- return $ P l r+ l <- (string' "A" $> UF (noAnn, noAnn)) <|> upairMacInner+ r <- (string' "I" $> UF (noAnn, noAnn)) <|> upairMacInner+ return $ UP l r unpairMac :: Parser Macro unpairMac = do string' "UN"- a <- pairMacInner+ a <- upairMacInner symbol' "R" (vns, fns) <- permute2Def (some note) (some note)- return $ UNPAIR (Macro.mapLeaves (zip vns fns) a)+ return $ UNPAIR (Macro.mapUnpairLeaves (zip vns fns) a) cadrMac :: Parser Macro cadrMac = lexeme $ do
src/Michelson/Parser/Value.hs view
@@ -16,9 +16,9 @@ import Prelude hiding (many, note, some, try) -import qualified Data.ByteString.Base16 as B16 import qualified Data.Char as Char import qualified Data.Map as Map+import qualified Text.Hex as Hex import Text.Megaparsec (anySingle, choice, customFailure, manyTill, satisfy, takeWhileP, try) import Text.Megaparsec.Char (char, string)@@ -80,10 +80,11 @@ bytesLiteral = do string "0x" hexdigits <- takeWhileP Nothing Char.isHexDigit- let (bytes, remain) = B16.decode $ encodeUtf8 hexdigits- if remain == ""- then return . U.ValueBytes . U.InternalByteString $ bytes- else customFailure OddNumberBytesException+ let mBytes = Hex.decodeHex hexdigits+ maybe+ (customFailure OddNumberBytesException)+ (return . U.ValueBytes . U.InternalByteString)+ mBytes intLiteral :: Parser (U.Value' op) intLiteral = try $ U.ValueInt <$> L.signed pass L.decimal
src/Michelson/Runtime.hs view
@@ -16,7 +16,6 @@ , parseExpandContract , readAndParseContract , prepareContract- , typeCheckWithDb -- * Re-exports , ContractState (..)@@ -63,8 +62,7 @@ import qualified Michelson.Parser as P import Michelson.Runtime.GState import Michelson.Runtime.TxData-import Michelson.TypeCheck- (SomeContract, TCError, typeCheckContract, typeCheckTopLevelType, typeVerifyTopLevelType)+import Michelson.TypeCheck (TCError, typeVerifyParameter) import Michelson.Typed (CreateContract(..), EpName, Operation'(..), SomeValue'(..), TransferTokens(..), convertContractCode, untypeValue)@@ -431,12 +429,8 @@ gs <- use esGState originationNonce <- use esOriginationNonce - typedContract <- liftEither $ first EEIllTypedContract $- typeCheckContract (extractAllContracts gs) (ooContract origination)- typedStorage <- liftEither $ first EEIllTypedStorage $- typeCheckTopLevelType- (extractAllContracts gs) (U.contractStorage $ ooContract origination)- (ooStorage origination)+ contractState <- liftEither $ mkContractState EEIllTypedContract EEIllTypedStorage+ (ooBalance origination, ooContract origination, ooStorage origination) let originatorAddress = ooOriginator origination originatorBalance <- case gsAddresses gs ^. at originatorAddress of Nothing -> throwError (EEUnknownManager originatorAddress)@@ -450,13 +444,13 @@ let address = mkContractAddress opHash $ OriginationIndex originationNonce updates =- [ GSAddAddress address (ASContract $ mkContractState typedContract typedStorage)+ [ GSAddAddress address (ASContract contractState) , GSSetBalance originatorAddress originatorBalance , GSIncrementCounter ] case applyUpdates updates gs of Left _ ->- throwError $ EEAlreadyOriginated address $ mkContractState typedContract typedStorage+ throwError $ EEAlreadyOriginated address contractState Right newGS -> do esGState .= newGS esOriginationNonce += 1@@ -464,14 +458,6 @@ esLog <>= ExecutorLog updates [] return address- where- mkContractState typedContract typedStorage = ContractState- { csBalance = ooBalance origination- , csStorage = ooStorage origination- , csContract = ooContract origination- , csTypedContract = Just typedContract- , csTypedStorage = Just typedStorage- } -- | Execute a transfer operation. executeTransfer@@ -523,12 +509,12 @@ newBalance = oldBalance `unsafeAddMutez` tdAmount txData upd = GSSetBalance addr newBalance onlyUpdates [upd]- (Just (ASContract cs), _) -> do+ (Just (ASContract (ContractState {..})), _) -> 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+ newBalance = csBalance `unsafeAddMutez` tdAmount txData contractEnv = ContractEnv { ceNow = now , ceMaxSteps = remainingSteps@@ -543,13 +529,11 @@ } epName = tdEntrypoint txData - SomeContractAndStorage typedContract typedStorage- <- liftEither $ getTypedContractAndStorage EEIllTypedContract EEIllTypedStorage gs cs- T.MkEntryPointCallRes _ epc- <- T.mkEntryPointCall epName (T.cParamNotes typedContract)+ T.MkEntrypointCallRes _ epc+ <- T.mkEntrypointCall epName (T.cParamNotes csContract) & maybe (throwError $ EEUnknownEntrypoint epName) pure typedParameter <- liftEither $ first EEIllTypedParameter $- typeVerifyTopLevelType existingContracts (tdParameter txData)+ typeVerifyParameter existingContracts (tdParameter txData) iur@InterpretResult { iurOps = sideEffects , iurNewStorage = newValue@@ -557,16 +541,15 @@ } <- liftEither $ first (EEInterpreterFailed addr) $ handleContractReturn $- interpret (T.cCode typedContract) epc- typedParameter typedStorage contractEnv+ interpret (T.cCode csContract) epc+ typedParameter csStorage contractEnv let- newValueU = untypeValue newValue updBalance- | newBalance == csBalance cs = Nothing+ | newBalance == csBalance = Nothing | otherwise = Just $ GSSetBalance addr newBalance updStorage- | SomeValue newValue == SomeValue typedStorage = Nothing- | otherwise = Just $ GSSetStorageValue addr newValueU (SomeValue newValue)+ | SomeValue newValue == SomeValue csStorage = Nothing+ | otherwise = Just $ GSSetStorageValue addr newValue updates = catMaybes [ updBalance , updStorage@@ -586,18 +569,6 @@ esLog <>= ExecutorLog updates (maybe mempty (one . (addr, )) maybeInterpretRes) return $ mapMaybe (convertOp addr) sideEffects--------------------------------------------------------------------------------- TypeCheck-------------------------------------------------------------------------------typeCheckWithDb- :: FilePath- -> U.Contract- -> IO (Either TCError SomeContract)-typeCheckWithDb dbPath morleyContract = do- gState <- readGState dbPath- pure . typeCheckContract (extractAllContracts gState) $ morleyContract ---------------------------------------------------------------------------- -- Simple helpers
src/Michelson/Runtime/GState.hs view
@@ -8,10 +8,7 @@ ( -- * Auxiliary types ContractState (..)- , getTypedContract- , getTypedStorage- , SomeContractAndStorage (..)- , getTypedContractAndStorage+ , mkContractState , AddressState (..) , asBalance @@ -56,16 +53,16 @@ import Data.List.NonEmpty ((!!)) import qualified Data.Map.Strict as Map import Data.Typeable ((:~:)(..), eqT)-import Fmt ((+|), (|+), (||+))-import Formatting.Buildable (Buildable(build))+import Fmt (Buildable(build), (+|), (|+), (||+)) import System.IO.Error (IOError, isDoesNotExistError) import Michelson.TypeCheck- (SomeContract(..), TCError, TcOriginatedContracts, typeCheckContract, typeCheckTopLevelType)-import Michelson.Typed (SomeValue, SomeValue'(..))+ (SomeContract(..), SomeParamType(..), TCError(..), TcOriginatedContracts, typeCheckContract,+ typeCheckStorage)+import Michelson.Typed (SomeValue'(..)) import qualified Michelson.Typed as T import Michelson.Typed.Scope-import Michelson.Untyped (Contract, ParameterType, Value, contractParameter, contractStorage)+import Michelson.Untyped (Contract, Value, contractStorage) import Tezos.Address (Address(..), ContractHash) import Tezos.Core (ChainId, Mutez, divModMutezInt, dummyChainId) import Tezos.Crypto@@ -73,53 +70,56 @@ import Util.Lens -- | State of a contract with code.-data ContractState = ContractState+data ContractState =+ forall cp st. (ParameterScope cp, StorageScope st) => ContractState { csBalance :: Mutez -- ^ Amount of mutez owned by this contract.- , csStorage :: Value+ , csContract :: T.Contract cp st+ -- ^ Contract itself.+ , csStorage :: T.Value st -- ^ Storage value associated with this contract.- , csContract :: Contract- -- ^ Contract itself (untyped).- , csTypedContract :: (Maybe SomeContract)- , csTypedStorage :: (Maybe SomeValue)- -- ^ We keep typed representation of contract code- -- and storage in form, that hides their actual type- -- in order to simplify the rest of the code- -- (e.g. avoid type parameters for `ContractState` and so on).- -- They are made optional in order to perform safe parsing- -- from JSON (we simply return `Nothing` in this parser and use- -- `getTypedStorage` or `getTypedContract` that optionally typecheck- -- storage or contract code). } deriving stock instance Show ContractState +mkContractState+ :: (TCError -> err)+ -> (TCError -> err)+ -> (Mutez, Contract, Value)+ -> Either err ContractState+mkContractState liftContractErr liftStorageErr (balance, uContract, uStorage) = do+ SomeContract (contract@T.Contract{} :: T.Contract cp st) <- first liftContractErr $ typeCheckContract uContract+ SomeValue (storage :: T.Value st') <- first liftStorageErr $ typeCheckStorage (contractStorage uContract) uStorage+ case eqT @st @st' of+ Just Refl -> pure $ ContractState balance contract storage+ _ -> Left $ liftStorageErr (TCContractError "Storage type does not match the contract in runtime state" Nothing)+ instance ToJSON ContractState where- toJSON ContractState{..} = object+ toJSON ContractState{..} =+ object [ "balance" .= csBalance- , "storage" .= csStorage- , "contract" .= csContract+ , "storage" .= T.untypeValue csStorage+ , "contract" .= T.convertContract csContract ] -- These instance is a bit hacky because it is quite painful to -- write proper JSON instances for typed `Instr` and `Value` so -- we typecheck untyped representation instead of parsing. instance FromJSON ContractState where- parseJSON = withObject "contractstate" $ \o -> do- csBalance <- o .: "balance"- csStorage <- o .: "storage"- csContract <- o .: "contract"- let csTypedContract = Nothing- let csTypedStorage = Nothing- return ContractState {..}+ parseJSON =+ withObject "contractstate" $ \o -> do+ (balance :: Mutez) <- o .: "balance"+ (uStorage :: Value) <- o .: "storage"+ (uContract :: Contract) <- o .: "contract"+ case mkContractState id id (balance, uContract, uStorage) of+ Right r -> return r+ Left err -> fail $ "Unable to parse `ContractState`: " <> (show err) instance Buildable ContractState where build ContractState{..} = "Contractstate:\n csBalance: " +| csBalance |+- "\n csStorage: " +| csStorage |+- "\n csContract: " +| csContract |+- "\n csTypedContract: " +| csTypedContract ||+- "\n csTypedStorage: " +| csTypedStorage ||+ ""+ "\n csContract: " +| T.untypeValue csStorage ||++ "\n csStorage: " +| T.convertContract csContract ||+ "" -- | State of an arbitrary address. data AddressState@@ -160,37 +160,9 @@ deriveJSON morleyAesonOptions ''GState -getTypedContract :: GState -> ContractState -> Either TCError SomeContract-getTypedContract gs ContractState{..} =- typeCheckContract (extractAllContracts gs) csContract -getTypedStorage :: GState -> ContractState -> Either TCError SomeValue-getTypedStorage gs ContractState{..} =- typeCheckTopLevelType (extractAllContracts gs) (contractStorage csContract) csStorage --- [#36] TODO: try to get rid of this type, 'ContractState' should become--- broader than it-data SomeContractAndStorage =- forall cp st. (ParameterScope cp, StorageScope st) => SomeContractAndStorage- { scsContract :: T.Contract cp st- , scsStorage :: T.Value st- } -getTypedContractAndStorage- :: (TCError -> err)- -> (TCError -> err)- -> GState- -> ContractState- -> Either err SomeContractAndStorage-getTypedContractAndStorage liftContractErr liftStorageErr gs cs = do- SomeContract (contract@T.Contract{} :: T.Contract cp st) <-- first liftContractErr $ getTypedContract gs cs- SomeValue (storage :: T.Value st') <-- first liftStorageErr $ getTypedStorage gs cs- Refl <- pure $ eqT @st @st'- ?: error "Storage type does not match the contract in runtime state"- return $ SomeContractAndStorage contract storage- -- | Number of genesis addresses. genesisAddressesNum :: Word genesisAddressesNum = 10@@ -280,20 +252,21 @@ } -- | Updates that can be applied to 'GState'.-data GStateUpdate- = GSAddAddress Address AddressState- | GSSetStorageValue Address Value SomeValue- | GSSetBalance Address Mutez- | GSIncrementCounter- deriving stock (Show)+data GStateUpdate where+ GSAddAddress :: Address -> AddressState -> GStateUpdate+ GSSetStorageValue :: StorageScope st => Address -> T.Value st -> GStateUpdate+ GSSetBalance :: Address -> Mutez -> GStateUpdate+ GSIncrementCounter :: GStateUpdate +deriving stock instance Show GStateUpdate+ instance Buildable GStateUpdate where build = \case GSAddAddress addr st -> "Add address " +| addr |+ " with state " +| st |+ ""- GSSetStorageValue addr val _ ->- "Set storage value of address " +| addr |+ " to " +| val |+ ""+ GSSetStorageValue addr tVal ->+ "Set storage value of address " +| addr |+ " to " +| T.untypeValue tVal |+ "" GSSetBalance addr balance -> "Set balance of address " +| addr |+ " to " +| balance |+ "" GSIncrementCounter ->@@ -303,6 +276,7 @@ = GStateAddressExists Address | GStateUnknownAddress Address | GStateNotContract Address+ | GStateStorageNotMatch Address deriving stock (Show) instance Buildable GStateUpdateError where@@ -311,6 +285,9 @@ GStateAddressExists addr -> "Address already exists: " <> build addr GStateUnknownAddress addr -> "Unknown address: " <> build addr GStateNotContract addr -> "Address doesn't have contract: " <> build addr+ GStateStorageNotMatch addr ->+ "Storage type does not match the contract in run-time state\+ \ when updating new storage value to address: " <> build addr -- | Apply 'GStateUpdate' to 'GState'. applyUpdate :: GStateUpdate -> GState -> Either GStateUpdateError GState@@ -318,8 +295,8 @@ \case GSAddAddress addr st -> maybeToRight (GStateAddressExists addr) . addAddress addr st- GSSetStorageValue addr newValue newTypedValue ->- setStorageValue addr newValue newTypedValue+ GSSetStorageValue addr newValue ->+ setStorageValue addr newValue GSSetBalance addr newBalance -> setBalance addr newBalance GSIncrementCounter -> Right . over gsCounterL (+1) @@ -335,18 +312,19 @@ where accounts = gsAddresses gs --- | Updare storage value associated with given address.-setStorageValue ::- Address -> Value -> SomeValue -> GState -> Either GStateUpdateError GState-setStorageValue addr newValue newTypedValue = updateAddressState addr modifier+-- | Update storage value associated with given address.+setStorageValue :: forall st. (StorageScope st) =>+ Address -> T.Value st -> GState -> Either GStateUpdateError GState+setStorageValue addr newValue = updateAddressState addr modifier where+ modifier :: AddressState -> Either GStateUpdateError AddressState modifier (ASSimple _) = Left (GStateNotContract addr)- modifier (ASContract cs) = Right $ ASContract $- cs { csStorage = newValue- , csTypedStorage = Just newTypedValue- }+ modifier (ASContract (ContractState b c (_ :: T.Value st') )) = do+ case (eqT @st @st') of+ Just Refl -> Right $ ASContract $ ContractState b c newValue+ _ -> Left $ GStateStorageNotMatch addr --- | Updare storage value associated with given address.+-- | Update storage value associated with given address. setBalance :: Address -> Mutez -> GState -> Either GStateUpdateError GState setBalance addr newBalance = updateAddressState addr (Right . modifier) where@@ -372,10 +350,10 @@ extractAllContracts = Map.fromList . mapMaybe extractContract . toPairs . gsAddresses where extractContract- :: (Address, AddressState) -> Maybe (ContractHash, ParameterType)+ :: (Address, AddressState) -> Maybe (ContractHash, SomeParamType) extractContract = \case (KeyAddress _, ASSimple {}) -> Nothing (KeyAddress _, _) -> error "broken GState"- (ContractAddress ca, ASContract cs) ->- Just (ca, contractParameter $ csContract cs)+ (ContractAddress ca, ASContract (ContractState{..})) ->+ Just (ca, SomeParamType sing $ T.cParamNotes $ csContract) (ContractAddress _, _) -> error "broken GState"
− src/Michelson/Test.hs
@@ -1,113 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module containing some utilities for testing Michelson contracts using--- Haskell testing frameworks (hspec and QuickCheck in particular).--- It's Morley testing EDSL.--module Michelson.Test- ( -- * Importing a contract- specWithContract- , specWithTypedContract- , specWithUntypedContract- , testTreesWithContract- , testTreesWithUntypedContract- , testTreesWithTypedContract- , concatTestTrees- , importContract- , importUntypedContract-- -- * Unit testing- , ContractReturn- , ContractPropValidator- , contractProp- , contractPropVal- , validateSuccess- , validateStorageIs- , validateMichelsonFailsWith-- -- * Integrational testing- -- ** Testing engine- , IntegrationalScenario- , IntegrationalScenarioM- , integrationalTestExpectation- , integrationalTestProp- , originate- , tOriginate- , transfer- , tTransfer- , integrationalFail- , unexpectedInterpreterError- , setMaxSteps- , setNow- , rewindTime- , withSender- , setChainId- , branchout- , (?-)-- -- ** Validators- , expectNoUpdates- , expectNoStorageUpdates- , expectStorageUpdate- , expectStorageUpdateConst- , expectBalance- , expectStorage- , expectStorageConst- , tExpectStorageConst-- -- ** Errors- , attempt- , expectError- , catchExpectedError- , expectGasExhaustion- , expectMichelsonFailed-- -- ** Various- , TxData (..)- , genesisAddress-- -- * General utilities- , failedTest- , succeededTest- , eitherIsLeft- , eitherIsRight- , total- , meanTimeUpperBoundProp- , meanTimeUpperBoundPropNF-- -- * Re-exports- --- -- | These functions from "Time" are re-exported here to make it convenient to call- -- 'meanTimeUpperBoundProp' and 'meanTimeUpperBoundPropNF'.- , mcs, ms, sec, minute-- -- * Autodoc testing- , runDocTests- , testDocBasic- , excludeDocTests-- -- * Dummy values- , dummyContractEnv-- -- * Arbitrary data- , minTimestamp- , maxTimestamp- , midTimestamp-- -- * Deprecated- , integrationalTestProperty- , failedProp- , succeededProp- , qcIsLeft- , qcIsRight- ) where--import Michelson.Doc.Test-import Michelson.Test.Dummy-import Michelson.Test.Gen-import Michelson.Test.Import-import Michelson.Test.Integrational-import Michelson.Test.Unit-import Michelson.Test.Util
− src/Michelson/Test/Dummy.hs
@@ -1,62 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Dummy data to be used in tests where it's not essential.--module Michelson.Test.Dummy- ( dummyNow- , dummyMaxSteps- , dummyContractEnv- , dummyOrigination- ) where--import Michelson.Interpret (ContractEnv(..), RemainingSteps)-import Michelson.Runtime.GState (genesisAddress)-import Michelson.Untyped-import Tezos.Core (Timestamp(..), dummyChainId, unsafeMkMutez)---- | Dummy timestamp, can be used to specify current `NOW` value or--- maybe something else.-dummyNow :: Timestamp-dummyNow = Timestamp 100---- | Dummy value for maximal number of steps a contract can--- make. Intentionally quite large, because most likely if you use--- dummy value you don't want the interpreter to stop due to gas--- exhaustion. On the other hand, it probably still prevents the--- interpreter from working for eternity.-dummyMaxSteps :: RemainingSteps-dummyMaxSteps = 100500---- | Dummy 'ContractEnv' with some reasonable hardcoded values. You--- can override values you are interested in using record update--- syntax.-dummyContractEnv :: ContractEnv-dummyContractEnv = ContractEnv- { ceNow = dummyNow- , ceMaxSteps = dummyMaxSteps- , ceBalance = unsafeMkMutez 100- , ceContracts = mempty- , ceSelf = genesisAddress- , ceSource = genesisAddress- , ceSender = genesisAddress- , ceAmount = unsafeMkMutez 100- , ceChainId = dummyChainId- , ceOperationHash = Nothing- }---- | 'OriginationOperation' with most data hardcoded to some--- reasonable values. Contract and initial values must be passed--- explicitly, because otherwise it hardly makes sense.-dummyOrigination ::- Value- -> Contract- -> OriginationOperation-dummyOrigination storage contract = OriginationOperation- { ooOriginator = genesisAddress- , ooDelegate = Nothing- , ooBalance = unsafeMkMutez 100- , ooStorage = storage- , ooContract = contract- }
− src/Michelson/Test/Gen.hs
@@ -1,128 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# OPTIONS_GHC -Wno-orphans #-}---- | Utilities for arbitrary data generation in property tests.------ == Deprecation Notice------ At the moment, this module exposes both Hedgehog generators and QuickCheck `Arbitrary`--- instances for convenience.--- However, `Arbitrary` instances should be considered deprecated and will eventually be removed.--module Michelson.Test.Gen- ( minTimestamp- , maxTimestamp- , midTimestamp- , genMutez- , genTimestamp- , genValueKeyHash- , genValueMutez- , genValueInt- , genValueList- , genValueUnit- , genValuePair- , genValueTimestamp- ) where--import Data.Time.Calendar (Day, addDays, diffDays)-import Data.Time.Clock (UTCTime(..))-import Data.Time.Format (defaultTimeLocale, parseTimeM)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.QuickCheck (Arbitrary(..), choose)--import Michelson.Typed (KnownT, T(..), Value'(..))-import Tezos.Core- (Mutez(..), Timestamp, timestampFromSeconds, timestampFromUTCTime, timestampToSeconds,- unsafeMkMutez)-import Tezos.Crypto (genKeyHash)--instance Arbitrary (Value' instr 'TKeyHash) where- arbitrary = VKeyHash <$> arbitrary-instance Arbitrary (Value' instr 'TMutez) where- arbitrary = VMutez <$> arbitrary-instance Arbitrary (Value' instr 'TInt) where- arbitrary = VInt <$> arbitrary-instance (KnownT a, Arbitrary (Value' instr a)) =>- Arbitrary (Value' instr ('TList a)) where- arbitrary = VList <$> arbitrary-instance Arbitrary (Value' instr 'TUnit) where- arbitrary = pure VUnit-instance (Arbitrary (Value' instr a), Arbitrary (Value' instr b))- => Arbitrary (Value' instr ('TPair a b)) where- arbitrary = VPair ... (,) <$> arbitrary <*> arbitrary--genValueKeyHash :: MonadGen m => m (Value' instr 'TKeyHash)-genValueKeyHash = VKeyHash <$> genKeyHash--genValueMutez :: MonadGen m => m (Value' instr 'TMutez)-genValueMutez = VMutez <$> genMutez--genValueInt :: MonadGen m => m (Value' instr 'TInt)-genValueInt = VInt <$> Gen.integral (Range.linearFrom 0 -1000 1000)--genValueList :: (MonadGen m, KnownT a) => m (Value' instr a) -> m (Value' instr ('TList a))-genValueList genA = VList <$> Gen.list (Range.linear 0 100) genA--genValueUnit :: Applicative m => m (Value' instr 'TUnit)-genValueUnit = pure VUnit--genValuePair :: MonadGen m => m (Value' instr a) -> m (Value' instr b) -> m (Value' instr ('TPair a b))-genValuePair genA genB = VPair ... (,) <$> genA <*> genB---minDay :: Day-minDay = fromMaybe (error "failed to parse day 2008-11-01") $- parseTimeM True defaultTimeLocale "%Y-%-m-%-d" "2008-11-01"--maxDay :: Day-maxDay = fromMaybe (error "failed to parse day 2024-11-01") $- parseTimeM True defaultTimeLocale "%Y-%-m-%-d" "2024-11-01"--minSec :: Integer-minSec = 0--maxSec :: Integer-maxSec = 86399---- | Minimal (earliest) timestamp used for @Arbitrary (CValue 'CTimestamp)@-minTimestamp :: Timestamp-minTimestamp = timestampFromUTCTime $ UTCTime minDay (fromInteger minSec)---- | Maximal (latest) timestamp used for @Arbitrary (CValue 'CTimestamp)@-maxTimestamp :: Timestamp-maxTimestamp = timestampFromUTCTime $ UTCTime maxDay (fromInteger maxSec)---- | Median of 'minTimestamp' and 'maxTimestamp'.--- Useful for testing (exactly half of generated dates will be before and after--- this date).-midTimestamp :: Timestamp-midTimestamp = timestampFromUTCTime $- UTCTime ( ((maxDay `diffDays` minDay) `div` 2) `addDays` minDay)- (fromInteger $ (maxSec - minSec) `div` 2)--instance Arbitrary (Value' instr 'TTimestamp) where- arbitrary = VTimestamp <$> arbitrary--instance Arbitrary Mutez where- arbitrary = unsafeMkMutez <$> choose (unMutez minBound, unMutez maxBound)--instance Arbitrary Timestamp where- arbitrary =- timestampFromSeconds <$>- choose (timestampToSeconds minTimestamp, timestampToSeconds maxTimestamp)--genValueTimestamp :: MonadGen m => m (Value' instr 'TTimestamp)-genValueTimestamp = VTimestamp <$> genTimestamp--genMutez :: MonadGen m => m Mutez-genMutez = unsafeMkMutez <$> Gen.word64 (Range.linear (unMutez minBound) (unMutez maxBound))--genTimestamp :: MonadGen m => m Timestamp-genTimestamp =- timestampFromSeconds <$> Gen.integral- (Range.linear (timestampToSeconds minTimestamp) (timestampToSeconds maxTimestamp))
− src/Michelson/Test/Import.hs
@@ -1,192 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Functions to import contracts to be used in tests.--module Michelson.Test.Import- (- -- * Read, parse, typecheck- readContract- , importContract- , importUntypedContract- , ImportContractError (..)-- -- * Tasty helpers- , testTreesWithContract- , testTreesWithTypedContract- , testTreesWithUntypedContract- , concatTestTrees-- -- * HSpec helpers- , specWithContract- , specWithTypedContract- , specWithUntypedContract- ) where--import Control.Exception (IOException)-import Data.Singletons (demote)-import Data.Typeable ((:~:)(..), eqT)-import Fmt (Buildable(build), pretty, (+|), (|+))-import Test.Hspec (Spec, describe, expectationFailure, it, runIO)-import Test.HUnit (assertFailure)-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)--import Michelson.Parser.Error (ParserException(..))-import Michelson.Runtime (parseExpandContract, prepareContract)-import Michelson.TypeCheck (SomeContract(..), TCError, typeCheckContract)-import Michelson.Typed (Contract(..), KnownT, toUType)-import qualified Michelson.Untyped as U-import Util.IO--------------------------------------------------------------------------------- tasty helpers--------------------------------------------------------------------------------- | Import contract and use to create test trees. Both versions of contract are--- passed to the callback function (untyped and typed).------ If contract's import fails, a tree with single failing test will be generated--- (so test tree will likely be generated unexceptionally, but a failing--- result will notify about problem).-testTreesWithContract- :: (Each '[KnownT] [cp, st], HasCallStack)- => FilePath -> ((U.Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree]-testTreesWithContract = testTreesWithContractImpl importContract---- | Like 'testTreesWithContract' but supplies only untyped contract.-testTreesWithUntypedContract- :: HasCallStack- => FilePath -> (U.Contract -> IO [TestTree]) -> IO [TestTree]-testTreesWithUntypedContract =- testTreesWithContractImpl importUntypedContract---- | Like 'testTreesWithContract' but supplies only typed contract.-testTreesWithTypedContract- :: (Each '[KnownT] [cp, st], HasCallStack)- => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree]-testTreesWithTypedContract =- testTreesWithContractImpl (fmap snd . importContract)--testTreesWithContractImpl- :: HasCallStack- => (FilePath -> IO contract)- -> FilePath- -> (contract -> IO [TestTree])- -> IO [TestTree]-testTreesWithContractImpl doImport file testImpl =- saferImport doImport file >>= \case- Left err -> pure [testCase ("Import contract " <> file) $ assertFailure err]- Right contract -> testImpl contract---- A helper function which allows you to use multiple--- 'testTreesWithTypedContract' in a single top-level test with type--- 'IO [TestTree]'.-concatTestTrees :: [IO [TestTree]] -> IO [TestTree]-concatTestTrees = fmap concat . sequence--------------------------------------------------------------------------------- hspec helpers--------------------------------------------------------------------------------- | Import contract and use it in the spec. Both versions of contract are--- passed to the callback function (untyped and typed).------ If contract's import fails, a spec with single failing expectation--- will be generated (so tests will likely run unexceptionally, but a failing--- result will notify about problem).-specWithContract- :: (Each '[KnownT] [cp, st], HasCallStack)- => FilePath -> ((U.Contract, Contract cp st) -> Spec) -> Spec-specWithContract = specWithContractImpl importContract---- | A version of 'specWithContract' which passes only the typed--- representation of the contract.-specWithTypedContract- :: (Each '[KnownT] [cp, st], HasCallStack)- => FilePath -> (Contract cp st -> Spec) -> Spec-specWithTypedContract = specWithContractImpl (fmap snd . importContract)--specWithUntypedContract :: FilePath -> (U.Contract -> Spec) -> Spec-specWithUntypedContract = specWithContractImpl importUntypedContract--specWithContractImpl- :: HasCallStack- => (FilePath -> IO contract) -> FilePath -> (contract -> Spec) -> Spec-specWithContractImpl doImport file execSpec =- either errorSpec (describe ("Test contract " <> file) . execSpec)- =<< runIO (saferImport doImport file)- where- errorSpec = it ("Import contract " <> file) . expectationFailure--------------------------------------------------------------------------------- Helpers--------------------------------------------------------------------------------- Catch some errors during contract import, we don't want the whole--- test suite to crash if something like that happens.-saferImport :: (FilePath -> IO contract) -> FilePath -> IO (Either String contract)-saferImport doImport file =- ((Right <$> doImport file)- `catch` \(e :: ImportContractError) -> pure $ Left $ displayException e)- `catch` \(e :: IOException) -> pure $ Left $ displayException e--------------------------------------------------------------------------------- Reading, parsing, typechecking-------------------------------------------------------------------------------readContract- :: forall cp st .- Each '[KnownT] [cp, st]- => FilePath- -> Text- -> Either ImportContractError (U.Contract, Contract cp st)-readContract filePath txt = do- contract <- first ICEParse $ parseExpandContract (Just filePath) txt- SomeContract (tContract@Contract{} :: Contract cp' st')- <- first ICETypeCheck $ typeCheckContract mempty contract- case (eqT @cp @cp', eqT @st @st') of- (Just Refl, Just Refl) -> pure (contract, tContract)- (Nothing, _) -> Left $- ICEUnexpectedParamType (U.contractParameter contract) (toUType $ demote @cp)- _ -> Left (ICEUnexpectedStorageType (U.contractStorage contract) (toUType $ demote @st))---- | Import contract from a given file path.------ This function reads file, parses and type checks a contract.--- Within the typechecking we assume that no contracts are originated,--- otherwise a type checking error will be caused.------ This function may throw 'IOException' and 'ImportContractError'.-importContract- :: forall cp st .- Each '[KnownT] [cp, st]- => FilePath -> IO (U.Contract, Contract cp st)-importContract file = either throwM pure =<< readContract file <$> readFileUtf8 file--importUntypedContract :: FilePath -> IO U.Contract-importUntypedContract = prepareContract . Just---- | Error type for 'importContract' function.-data ImportContractError- = ICEUnexpectedParamType U.ParameterType U.Type- | ICEUnexpectedStorageType U.Type U.Type- | ICEParse ParserException- | ICETypeCheck TCError- deriving stock (Show, Eq)--instance Buildable ImportContractError where- build =- \case- ICEUnexpectedParamType actual expected ->- "Unexpected parameter type: " +| actual |+- ", expected: " +| expected |+ ""- ICEUnexpectedStorageType actual expected ->- "Unexpected storage type: " +| actual |+- ", expected: " +| expected |+ ""- ICEParse e -> "Failed to parse the contract: " +| e |+ ""- ICETypeCheck e -> "The contract is ill-typed: " +| e |+ ""--instance Exception ImportContractError where- displayException = pretty
− src/Michelson/Test/Integrational.hs
@@ -1,601 +0,0 @@-{-# OPTIONS_GHC -Wno-deprecations #-}---- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Utilities for integrational testing.--- Example tests can be found in the 'morley-test' test suite.--module Michelson.Test.Integrational- (- -- * Re-exports- TxData (..)- -- * More genesis addresses which can be used in tests- , genesisAddress- , genesisAddress1- , genesisAddress2- , genesisAddress3- , genesisAddress4- , genesisAddress5- , genesisAddress6-- -- * Testing engine- , IntegrationalScenarioM- , IntegrationalScenario- , TestError (..)- , integrationalTestExpectation- , integrationalTestProp- , originate- , tOriginate- , transfer- , tTransfer- , integrationalFail- , unexpectedInterpreterError- , setMaxSteps- , modifyNow- , setNow- , rewindTime- , withSender- , setChainId- , branchout- , (?-)- , offshoot-- -- * Validators- , expectNoUpdates- , expectNoStorageUpdates- , expectStorageUpdate- , expectStorageUpdateConst- , expectBalance- , expectStorage- , expectStorageConst- , tExpectStorageConst-- -- * Errors- , attempt- , expectError- , catchExpectedError- , expectGasExhaustion- , expectMichelsonFailed-- -- * Lenses- , isGState-- -- * Deprecated- , integrationalTestProperty- ) where--import Control.Lens (assign, at, makeLenses, makeLensesFor, modifying, (%=), (.=), (<>=), (?=))-import Control.Monad.Except (Except, catchError, runExcept, throwError, withExcept)-import qualified Data.List as List-import Data.Map as Map (empty, insert, lookup)-import Fmt (Buildable(..), blockListF, listF, pretty, (+|), (|+))-import Hedgehog (MonadTest)-import Named ((:!), arg)-import Test.Hspec (Expectation, expectationFailure)-import qualified Test.QuickCheck as QC--import Michelson.Interpret (InterpretError(..), MichelsonFailed(..), RemainingSteps)-import Michelson.Runtime- (ExecutorError, ExecutorError'(..), ExecutorM, ExecutorOp(..), ExecutorRes(..),- executeGlobalOperations, executeOrigination, runExecutorM, withGlobalOperation)-import Michelson.Runtime.GState-import Michelson.Runtime.TxData-import Michelson.Test.Dummy-import Michelson.Test.Util (failedProp, failedTest, succeededProp, succeededTest)-import Michelson.TypeCheck (TCError)-import qualified Michelson.Typed as Typed-import Michelson.Typed.Scope (ParameterScope, StorageScope, properParameterEvi, withDict)-import Michelson.Untyped (Contract, EpName, OriginationOperation(..), Value)-import Tezos.Address (Address)-import Tezos.Core (ChainId, Mutez, Timestamp, timestampPlusSeconds, unsafeMkMutez)--------------------------------------------------------------------------------- Some internals (they are here because TH makes our very existence much harder)--------------------------------------------------------------------------------- | A result of an executed operation.-type ExecutorResOrError a = Either ExecutorError (ExecutorRes, a)-type ExecutorResOrError' = Either ExecutorError ExecutorRes--data InternalState = InternalState- { _isMaxSteps :: RemainingSteps- , _isNow :: Timestamp- , _isGState :: GState- , _isInterpreterLog :: [ExecutorResOrError']- -- ^ Store result of interpreted operations as they added.- , _isExecutorResult :: Maybe ExecutorRes- -- ^ Store the most recent result of interpreted operations.- , _isContractsNames :: Map Address Text- -- ^ Map from contracts addresses to humanreadable names.- , _isSender :: Maybe Address- -- ^ If set, all following transfers will be executed on behalf- -- of the given contract.- }--makeLenses ''InternalState---- | When using 'branch' function for building test scenarios - names--- of branches we are currently within.-newtype ScenarioBranchName = ScenarioBranchName { unTestBranch :: [Text] }--instance Buildable ScenarioBranchName where- build = mconcat . intersperse "/" . map build . unTestBranch--------------------------------------------------------------------------------- Interface--------------------------------------------------------------------------------- | A monad inside which integrational tests can be described using--- do-notation.-type IntegrationalScenarioM = StateT InternalState (Except ScenarioError)--type IntegrationalScenario = IntegrationalScenarioM ()--newtype ExpectedStorage = ExpectedStorage Value deriving stock (Show)-newtype ExpectedBalance = ExpectedBalance Mutez deriving stock (Show)--data AddressName = AddressName (Maybe Text) Address deriving stock (Show)--addrToAddrName :: Address -> InternalState -> AddressName-addrToAddrName addr iState =- AddressName (lookup addr (iState ^. isContractsNames)) addr--addrNameToAddr :: AddressName -> Address-addrNameToAddr (AddressName _ addr) = addr--instance Buildable AddressName where- build (AddressName mbName addr) =- build addr +| maybe "" (\cName -> " (" +|cName |+ ")") mbName--type IntegrationalExecutorError = ExecutorError' AddressName--data TestError- = InterpreterError IntegrationalExecutorError- | UnexpectedInterpreterError Text IntegrationalExecutorError- | UnexpectedTypeCheckError TCError- | ExpectingInterpreterToFail- | IncorrectUpdates TestError [GStateUpdate]- | IncorrectStorageUpdate AddressName Text- | InvalidStorage AddressName ExpectedStorage Text- | StoragePredicateMismatch AddressName Text- | InvalidBalance AddressName ExpectedBalance Text- | UnexpectedUpdates (NonEmpty GStateUpdate)- | ValidatingEmptyScenario- | CustomTestError Text- deriving stock (Show)--instance Buildable TestError where- build (InterpreterError iErr) =- "Interpreter failed: " +| iErr |+ ""- build (UnexpectedInterpreterError reason iErr) =- "Unexpected interpreter error. Reason: " +| reason |+ ". Got: " +| iErr |+ ""- build (UnexpectedTypeCheckError tcErr) =- "Unexpected type check error. Reason: " +| tcErr |+ ""- build ExpectingInterpreterToFail =- "Interpreter unexpectedly didn't fail"- build (IncorrectUpdates vErr updates) =- "Updates are incorrect: " +| vErr |+ " . Updates are:"- +| blockListF updates |+ ""- build (IncorrectStorageUpdate addr msg) =- "Storage of " +| addr |+ " is updated incorrectly: " +| msg |+ ""- build (InvalidStorage addr (ExpectedStorage expected) msg) =- "Expected " +| addr |+ " to have storage " +| expected |+ ", but " +| msg |+ ""- build (StoragePredicateMismatch addr msg) =- "Expected " +| addr |+ " to have storage that matches the predicate, but" +| msg |+ ""- build (InvalidBalance addr (ExpectedBalance expected) msg) =- "Expected " +| addr |+ " to have balance " +| expected |+ ", but " +| msg |+ ""- build (UnexpectedUpdates updates) =- "Did not expect certain updates, but there are some: " +| listF updates |+ ""- build ValidatingEmptyScenario =- "Validating empty scenario"- build (CustomTestError msg) = pretty msg--instance Exception TestError where- displayException = pretty---- | Overall information about test scenario error.-data ScenarioError = ScenarioError- { _seBranch :: ScenarioBranchName- , _seError :: TestError- }--makeLensesFor [("_seBranch", "seBranch")] ''ScenarioError--instance Buildable ScenarioError where- build (ScenarioError br err) =- let builtBranch- | nullScenarioBranch br = ""- | otherwise = "In '" +| br |+ "' branch:\n"- in builtBranch <> build err---- | Integrational test that executes given operations and validates--- them. It can fail using 'Expectation' capability.--- It starts with 'initGState' and some reasonable dummy values for--- gas limit and current timestamp. You can update blockchain state--- by performing some operations.-integrationalTestExpectation- :: HasCallStack- => IntegrationalScenario -> Expectation-integrationalTestExpectation =- integrationalTest (maybe pass (expectationFailure . pretty))---- | Integrational test similar to 'integrationalTestExpectation'.--- It can fail using 'QC.Property' capability.--- It can be used with QuickCheck's @forAll@ to make a--- property-based test with arbitrary data.-integrationalTestProperty :: IntegrationalScenario -> QC.Property-integrationalTestProperty =- integrationalTest (maybe succeededProp (failedProp . pretty))-{-# DEPRECATED integrationalTestProperty "Use 'integrationalTestProp' instead." #-}---- | Integrational test similar to 'integrationalTestExpectation'.--- It can fail using 'Property' capability.--- It can be used with Hedgehog's @forAll@ to make a--- property-based test with arbitrary data.-integrationalTestProp :: MonadTest m => IntegrationalScenario -> m ()-integrationalTestProp scenario =- integrationalTest (maybe succeededTest (failedTest . pretty)) scenario---- | Helper function which provides the results of the given operations.-interpret :: ExecutorM a -> IntegrationalScenarioM (ExecutorResOrError a)-interpret action = do- now <- use isNow- maxSteps <- use isMaxSteps- gState <- use isGState- let interpretedResult = runExecutorM now maxSteps gState action- whenRight interpretedResult $ \(result, _) -> isGState .= _erGState result- return interpretedResult---- | Interprets provided list of operations.-registerInterpretation :: [ExecutorOp] -> IntegrationalScenarioM ()-registerInterpretation ops =- interpret (executeGlobalOperations ops) <&> fmap fst >>= putResult---- | Originate a contract with given initial storage and balance. Its--- address is returned.-originate :: Contract -> Text -> Value -> Mutez -> IntegrationalScenarioM Address-originate contract contractName value balance = do- is <- get- result <- interpret $ withGlobalOperation (OriginateOp origination)- $ executeOrigination origination- putResult $ fmap fst result- address <- either (interpreterError is) (pure . snd) $ result-- isContractsNames %= insert address contractName- return address- where- interpreterError :: InternalState -> ExecutorError -> IntegrationalScenarioM a- interpreterError is = integrationalFail . InterpreterError . mkError is-- origination = (dummyOrigination value contract) {ooBalance = balance}---- | Like 'originate', but for typed contract and value.-tOriginate ::- (ParameterScope cp, StorageScope st)- => Typed.Contract cp st- -> Text- -> Typed.Value st- -> Mutez- -> IntegrationalScenarioM Address-tOriginate contract name value balance =- originate (Typed.convertContract contract) name- (Typed.untypeValue value) balance---- | Transfer tokens to a given address.-transfer :: TxData -> Address -> IntegrationalScenarioM ()-transfer txData destination = do- mSender <- use isSender- let unwrappedData = maybe id (set tdSenderAddressL) mSender txData- registerInterpretation [TransferOp destination unwrappedData]---- | Similar to 'transfer', for typed values.--- Note that it works with untyped 'Address' and does not check that--- entrypoint with given name is present and has the expected type.--- Passed value must correspond to the entrypoint argument type, not--- the parameter type of the contract (and must be unit for implicit--- accounts).-tTransfer- :: forall arg.- (ParameterScope arg)- => "from" :! Address- -> "to" :! Address- -> Mutez- -> EpName- -> Typed.Value arg- -> IntegrationalScenarioM ()-tTransfer (arg #from -> from) (arg #to -> to) money epName param =- let txData = TxData- { tdSenderAddress = from- , tdParameter =- withDict (properParameterEvi @arg) $- Typed.untypeValue param- , tdEntrypoint = epName- , tdAmount = money- }- in transfer txData to---- | Validator for integrational testing that expects successful execution.-validate- :: (InternalState -> GState -> [GStateUpdate] -> Either TestError ())- -> IntegrationalScenario-validate validator = do- iState <- get- interpreterResult <- use isExecutorResult- case interpreterResult of- Nothing -> integrationalFail ValidatingEmptyScenario- Just result -> do- case validator iState (_erGState result) (_erUpdates result) of- Left bad -> integrationalFail $ IncorrectUpdates bad (_erUpdates result)- Right () -> pass---- | Just fail with given error.-integrationalFail :: TestError -> IntegrationalScenarioM anything-integrationalFail = throwError . ScenarioError emptyScenarioBranch---- | Fail a test because an interpreter error happened unexpectedly, with the given reason.-unexpectedInterpreterError :: ExecutorError -> Text -> IntegrationalScenarioM a-unexpectedInterpreterError err reason = do- iState <- get- integrationalFail $ UnexpectedInterpreterError reason (mkError iState err)---- | Make all further interpreter calls use the modified timestamp as the current one.-modifyNow :: (Timestamp -> Timestamp) -> IntegrationalScenarioM ()-modifyNow = modifying isNow---- | Make all further interpreter calls use the given timestamp as the current one.-setNow :: Timestamp -> IntegrationalScenarioM ()-setNow time = modifyNow (const time)---- | Increase current time by the given number of seconds.-rewindTime :: Integer -> IntegrationalScenarioM ()-rewindTime interval = modifyNow (flip timestampPlusSeconds interval)---- | Make all further interpreter calls use the given gas limit.-setMaxSteps :: RemainingSteps -> IntegrationalScenarioM ()-setMaxSteps = assign isMaxSteps---- | Pretend that given address initiates all the transfers within the--- code block (i.e. @SENDER@ instruction will return this address).-withSender :: Address -> IntegrationalScenarioM a -> IntegrationalScenarioM a-withSender addr scenario = do- prevSender <- use isSender- isSender ?= addr- scenario <* (isSender .= prevSender)---- | Make all further interpreter calls use the given chain id.-setChainId :: ChainId -> IntegrationalScenarioM ()-setChainId = assign (isGState . gsChainIdL)---- | Put an interpreted result to InternalState.-putResult :: ExecutorResOrError' -> IntegrationalScenarioM ()-putResult resOrErr = do- isInterpreterLog <>= one resOrErr- case resOrErr of- Right res -> isExecutorResult .= Just res- Left err -> do- iState <- get- integrationalFail $ InterpreterError $ mkError iState err---- | Make branch names for a case when we are not within any branch.-emptyScenarioBranch :: ScenarioBranchName-emptyScenarioBranch = ScenarioBranchName []---- | Add a new branch element to names provided by inner 'branch' calls.-appendScenarioBranch :: Text -> ScenarioBranchName -> ScenarioBranchName-appendScenarioBranch brName (ScenarioBranchName branches) =- ScenarioBranchName (brName : branches)--nullScenarioBranch :: ScenarioBranchName -> Bool-nullScenarioBranch (ScenarioBranchName brs) = null brs---- | Execute multiple testing scenarios independently, basing--- them on scenario built till this point.------ The following property holds for this function:------ @ pre >> branchout [a, b, c] = branchout [pre >> a, pre >> b, pre >> c] @.------ In case of property failure in one of the branches no following branch is--- executed.------ Providing empty list of scenarios to this function causes error;--- we do not require 'NonEmpty' here though for convenience.-branchout :: HasCallStack => [(Text, IntegrationalScenario)] -> IntegrationalScenario-branchout scenarios- | null scenarios = error "branch: empty list of scenarios provided"- | otherwise = do- st <- get- lift . forM_ scenarios $ \(name, scenario) ->- withExcept (seBranch %~ appendScenarioBranch name) $- evalStateT scenario st---- | Make a tuple with name without extra syntactic noise.-(?-) :: Text -> a -> (Text, a)-(?-) = (,)-infixr 0 ?----- | Test given scenario with the state gathered till this moment;--- if this scenario passes, go on as if it never happened.-offshoot :: Text -> IntegrationalScenario -> IntegrationalScenario-offshoot name scenario = do- st <- get- lift $- withExcept (seBranch %~ appendScenarioBranch name) $- evalStateT scenario st--------------------------------------------------------------------------------- Validators--------------------------------------------------------------------------------- | Check that there were no updates.-expectNoUpdates :: IntegrationalScenario-expectNoUpdates = validate $ \_ _ updates ->- maybe pass (throwError . UnexpectedUpdates) . nonEmpty $ updates---- | Check that there were no storage updates.-expectNoStorageUpdates :: IntegrationalScenario-expectNoStorageUpdates = validate $ \_ _ updates ->- maybe pass (throwError . UnexpectedUpdates) . nonEmpty $- filter isStorageUpdate updates- where- isStorageUpdate = \case- GSSetStorageValue {} -> True- _ -> False---- | Check that storage value satisfies the given predicate.-expectStorage- :: Address- -> (Value -> Either TestError ())- -> IntegrationalScenario-expectStorage addr predicate = validate $ \is gs _ ->- let intro = StoragePredicateMismatch (addrToAddrName addr is) in- case gsAddresses gs ^. at addr of- Just (ASContract cs) ->- predicate $ csStorage cs- Just (ASSimple {}) ->- Left $ intro $ "it's a simple address"- Nothing -> Left $ intro $ "it's unknown"---- | Check that storage value is updated for given address. Takes a--- predicate that is used to check the value.------ It works even if updates are not filtered (i. e. a value can be--- updated more than once).-expectStorageUpdate- :: Address- -> (Value -> Either TestError ())- -> IntegrationalScenario-expectStorageUpdate addr predicate = validate $ \is _ updates ->- case List.find checkAddr (reverse updates) of- Nothing -> Left $- IncorrectStorageUpdate (addrToAddrName addr is) "storage wasn't updated"- Just (GSSetStorageValue _ val _) ->- first (IncorrectStorageUpdate (addrToAddrName addr is) . pretty) $- predicate val- -- 'checkAddr' ensures that only 'GSSetStorageValue' can be found- Just _ -> error "expectStorageUpdate: internal error"- where- checkAddr (GSSetStorageValue addr' _ _) = addr' == addr- checkAddr _ = False---- | Like 'expectStorageUpdate', but expects a constant.-expectStorageUpdateConst- :: Address- -> Value- -> IntegrationalScenario-expectStorageUpdateConst addr expected = do- is <- get- let- predicate val- | val == expected = pass- | otherwise = Left $- IncorrectStorageUpdate (addrToAddrName addr is) (pretty expected)- expectStorageUpdate addr predicate---- | Check that eventually address has some particular storage value.-expectStorageConst :: Address -> Value -> IntegrationalScenario-expectStorageConst addr expected = do- is <- get- let- predicate val- | val == expected = pass- | otherwise = Left $- InvalidStorage (addrToAddrName addr is) (ExpectedStorage expected) (pretty val)- expectStorage addr predicate---- | Similar to 'expectStorageConst', for typed stuff.-tExpectStorageConst- :: forall st.- (StorageScope st)- => Address -> Typed.Value st -> IntegrationalScenario-tExpectStorageConst addr expected =- expectStorageConst addr (Typed.untypeValue expected)---- | Check that eventually address has some particular balance.-expectBalance :: Address -> Mutez -> IntegrationalScenario-expectBalance addr balance = validate $ \is gs _ ->- let realBalance = maybe (unsafeMkMutez 0) asBalance (gsAddresses gs ^. at addr) in- if realBalance == balance then pass- else- Left- $ InvalidBalance (addrToAddrName addr is) (ExpectedBalance balance)- $ "its actual balance is: " <> pretty realBalance----- | Attempt to run an action and return its result or, if interpretation fails, an error.-attempt :: IntegrationalScenarioM a -> IntegrationalScenarioM (Either ExecutorError a)-attempt ma = catchError (Right <$> ma) $ \case- ScenarioError _ (InterpreterError err) -> pure . Left $ addrNameToAddr <$> err- err -> throwError err---- | Run an action that is expected to fail.--- If the action fails, the test succeeds and the error is returned.--- If the action succeeds, the test fails.-expectError :: IntegrationalScenarioM a -> IntegrationalScenarioM ExecutorError-expectError scenario = catchExpectedError scenario pure---- | Run an action that is expected to fail.------ In @action `catchExpectedError` f@:--- If the action fails, @f@ is applied to the error.--- If the action succeeds, the test fails.-catchExpectedError- :: IntegrationalScenarioM a- -> (ExecutorError -> IntegrationalScenarioM b)- -> IntegrationalScenarioM b-catchExpectedError scenario handle =- attempt scenario >>= \case- Left err -> handle err- Right _ -> integrationalFail ExpectingInterpreterToFail---- | Check that interpreter failed due to gas exhaustion.-expectGasExhaustion :: ExecutorError -> IntegrationalScenario-expectGasExhaustion =- \case- EEInterpreterFailed _ (RuntimeFailure (MichelsonGasExhaustion, _)) -> pass- err -> unexpectedInterpreterError err "expected runtime failure due to gas exhaustion"---- | Expect that interpretation of contract with given address ended--- with [FAILED].-expectMichelsonFailed :: (MichelsonFailed -> Bool) -> Address -> ExecutorError -> IntegrationalScenario-expectMichelsonFailed predicate expectedAddr err =- case err of- EEInterpreterFailed actualAddr (RuntimeFailure (mf, _))- | expectedAddr /= actualAddr -> do- iState <- get- unexpectedInterpreterError err $- "expected runtime failure for contract with address "- <> pretty (addrToAddrName expectedAddr iState)- | not (predicate mf) -> unexpectedInterpreterError err "predicate failed"- | otherwise -> pass- _ -> unexpectedInterpreterError err "expected runtime failure"--------------------------------------------------------------------------------- Implementation of the testing engine-------------------------------------------------------------------------------initIS :: InternalState-initIS = InternalState- { _isNow = dummyNow- , _isMaxSteps = dummyMaxSteps- , _isGState = initGState- , _isInterpreterLog = mempty- , _isExecutorResult = Nothing- , _isContractsNames = Map.empty- , _isSender = Nothing- }--integrationalTest ::- (Maybe ScenarioError -> res)- -> IntegrationalScenario- -> res-integrationalTest howToFail scenario =- howToFail $ leftToMaybe $ runExcept (runStateT scenario initIS)--mkError- :: InternalState- -> ExecutorError- -> IntegrationalExecutorError-mkError is = fmap $ flip addrToAddrName is
− src/Michelson/Test/Unit.hs
@@ -1,135 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Utility functions for unit testing.--module Michelson.Test.Unit- ( ContractReturn- , ContractPropValidator- , contractProp- , contractPropVal- , contractHasEntryPoints- , matchContractEntryPoints- , mkEntrypointsMap- , hasEp- , validateSuccess- , validateStorageIs- , validateMichelsonFailsWith- ) where--import Data.List.NonEmpty (fromList)-import qualified Data.Map as Map-import Fmt ((+|), (|+))-import Test.Hspec.Expectations (Expectation, shouldBe, shouldSatisfy)-import Test.HUnit (Assertion, assertFailure, (@?=))--import Michelson.Interpret (ContractEnv, ContractReturn, MichelsonFailed(..), interpret)-import Michelson.Printer (printUntypedContract)-import Michelson.Runtime (parseExpandContract)-import Michelson.Typed (Contract, IsoValue(..), ToT)-import qualified Michelson.Typed as T-import Michelson.Untyped hiding (Contract)-import qualified Michelson.Untyped as U---- | Type for contract execution validation.------ It's a function which is supplied with contract execution output--- (failure or new storage with operation list).------ Function returns a property which type is designated by type variable @prop@--- and might be 'Test.QuickCheck.Property' or 'Test.Hspec.Expectation'--- or anything else relevant.-type ContractPropValidator st prop = ContractReturn st -> prop---- | ContractCode's property tester against given input.--- Takes contract environment, initial storage and parameter,--- interprets contract on this input and invokes validation function.-contractProp- :: ( IsoValue param, IsoValue storage- , ToT param ~ cp, ToT storage ~ st- , T.ParameterScope cp- )- => Contract cp st- -> ContractPropValidator st prop- -> ContractEnv- -> param- -> storage- -> prop-contractProp instr check env param initSt =- contractPropVal instr check env (toVal param) (toVal initSt)---- | Version of 'contractProp' which takes 'Val' as arguments instead--- of regular Haskell values.------ This function assumes that contract has no explicit default entrypoints--- and you always have to construct parameter manually; if you need to test--- contract calling specific entrypoints, use integrational testing defined--- by "Michelson.Test.Integrational" module.-contractPropVal- :: (T.ParameterScope cp)- => Contract cp st- -> ContractPropValidator st prop- -> ContractEnv- -> T.Value cp- -> T.Value st- -> prop-contractPropVal instr check env param initSt =- check $ interpret (T.cCode instr) T.epcCallRootUnsafe param initSt env---- | Check if entrypoint is present in `T`.-hasEp :: T -> (EpName, U.Type) -> Bool-hasEp (TOr lFieldAnn rFieldAnn lType@(Type lT _) rType@(Type rT _))- ep@(epNameToParamAnn -> epAnn, epType) = or- [ (epAnn == lFieldAnn && epType == lType)- , (epAnn == rFieldAnn && epType == rType)- , hasEp lT ep- , hasEp rT ep ]-hasEp _ _ = False---- | Check whether the given set of entrypoints is present in contract.-contractHasEntryPoints :: U.Contract -> Map EpName U.Type -> Bool-contractHasEntryPoints contract eps = isRight $ matchContractEntryPoints contract eps---- | Match the given contract with provided set of entrypoints, return left if some--- entrypoints were not found.-matchContractEntryPoints- :: HasCallStack- => U.Contract- -> Map EpName U.Type- -> Either (NonEmpty (EpName, U.Type)) ()-matchContractEntryPoints contract eps =- phi $ fromRight (error "Impossible") parsedContract- where- parsedContract = parseExpandContract Nothing- (toText $ printUntypedContract True contract)- phi (contractParameter -> ParameterType (Type t _) _) =- conv $ filter (\ep -> not (hasEp t ep)) (Map.toList eps)- 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- :: IsoValue st- => st -> ContractPropValidator (ToT st) Assertion-validateStorageIs expected (res, _) =- case res of- Left err ->- assertFailure $ "Unexpected interpretation failure: " +| err |+ ""- Right (_ops, got) ->- got @?= toVal expected---- | 'ContractPropValidator' that expects a given failure.-validateMichelsonFailsWith- :: IsoValue v- => v -> ContractPropValidator st Expectation-validateMichelsonFailsWith v (res, _) = res `shouldBe` Left (MichelsonFailedWith $ toVal v)
− src/Michelson/Test/Util.hs
@@ -1,251 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Testing utility functions used by testing framework itself or--- intended to be used by test writers.--module Michelson.Test.Util- ( leftToShowPanic- , leftToPrettyPanic- , failedTest- , succeededTest- , eitherIsLeft- , eitherIsRight- , total- , meanTimeUpperBoundProp- , meanTimeUpperBoundPropNF- , genEither- , genTuple2- , runGen- , roundtripTree-- -- * Re-exports- --- -- | These functions from "Time" are re-exported here to make it convenient to call- -- 'meanTimeUpperBoundProp' and 'meanTimeUpperBoundPropNF'.- , mcs, ms, sec, minute-- -- * Deprecated- , failedProp- , succeededProp- , qcIsLeft- , qcIsRight- , roundtripTest- ) where--import Criterion (Benchmarkable, benchmarkWith', nf, whnf)-import Criterion.Main (defaultConfig)-import Criterion.Types (SampleAnalysis(anMean), Verbosity(Quiet), reportAnalysis, verbosity)-import Data.Typeable (typeRep)-import Fmt (Buildable, pretty)-import Hedgehog- (Gen, MonadGen, MonadTest, Property, annotate, eval, evalIO, failure, forAll, property, success,- tripping, withTests)-import qualified Hedgehog.Gen as Gen-import Hedgehog.Internal.Gen (runGenT)-import qualified Hedgehog.Internal.Seed as Seed-import Hedgehog.Internal.Tree (TreeT(runTreeT), nodeValue)-import qualified Hedgehog.Range as Range-import Statistics.Types (Estimate(estPoint))-import Test.QuickCheck (Arbitrary)-import qualified Test.QuickCheck.Property as QC-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)-import qualified Test.Tasty.QuickCheck as TQC-import Text.Printf (printf)-import Time- (KnownDivRat, KnownUnitName, Microsecond, Millisecond, Minute, Nanosecond, Picosecond, RatioNat,- Second, Time, mcs, minute, ms, ns, sec, timeout, toUnit, unTime, unitNameVal)--leftToShowPanic :: (Show e, HasCallStack) => Either e a -> a-leftToShowPanic = either (error . show) id--leftToPrettyPanic :: (Buildable e, HasCallStack) => Either e a -> a-leftToPrettyPanic = either (error . pretty) id--------------------------------------------------------------------------------- Property--------------------------------------------------------------------------------- | A 'QC.Property' that always fails with given message.-failedProp :: Text -> QC.Property-failedProp r = QC.property $ QC.failed { QC.reason = toString r }-{-# DEPRECATED failedProp "Use 'failedtest' instead." #-}---- | A 'QC.Property' that always succeeds.-succeededProp :: QC.Property-succeededProp = QC.property True-{-# DEPRECATED succeededProp "Use 'succeededTest' instead." #-}---- | The 'QC.Property' holds on `Left a`.-qcIsLeft :: Show b => Either a b -> QC.Property-qcIsLeft = \case- Left _ -> succeededProp- Right x -> failedProp $ "expected Left, got Right (" <> show x <> ")"-{-# DEPRECATED qcIsLeft "Use 'eitherIsLeft' instead." #-}---- | The 'QC.Property' holds on `Right b`.-qcIsRight :: Show a => Either a b -> QC.Property-qcIsRight = \case- Right _ -> succeededProp- Left x -> failedProp $ "expected Right, got Left (" <> show x <> ")"-{-# DEPRECATED qcIsRight "Use 'eitherIsRight' instead." #-}---- | A 'Property' that always fails with given message.-failedTest :: (HasCallStack, MonadTest m) => Text -> m ()-failedTest r = withFrozenCallStack $ annotate (toString r) >> failure---- | A 'Property' that always succeeds.-succeededTest :: MonadTest m => m ()-succeededTest = success---- | The 'Property' holds on `Left a`.-eitherIsLeft :: (Show b, MonadTest m, HasCallStack) => Either a b -> m ()-eitherIsLeft = \case- Left _ -> succeededTest- Right x -> withFrozenCallStack $ failedTest $ "expected Left, got Right (" <> show x <> ")"---- | The 'Property' holds on `Right b`.-eitherIsRight :: (Show a, MonadTest m, HasCallStack) => Either a b -> m ()-eitherIsRight = \case- Right _ -> succeededTest- Left x -> withFrozenCallStack $ failedTest $ "expected Right, got Left (" <> show x <> ")"---- | Checks that a value is total, i.e., doesn't crash when evaluated,--- by reducing it to its normal form.------ Equivalent to QuickCheck's @total@.-total :: (MonadTest m, NFData a, HasCallStack) => a -> m a-total a = (withFrozenCallStack $ eval $ rnf a) $> a---- | Benchmarks the given function and checks that the mean time to evaluate to weak head--- normal form is under the given amount of time.------ This test fails if the benchmark takes longer than 30 seconds to run.-meanTimeUpperBoundProp- :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack)- => Time unit -> (a -> b) -> a -> Property-meanTimeUpperBoundProp upperBound run arg =- withFrozenCallStack $- checkReport upperBound $ whnf run arg---- | Benchmarks the given function and checks that the mean time to evaluate to--- normal form is under the given amount of time.------ This test aborts and fails if the benchmark takes longer than 120 seconds to run.-meanTimeUpperBoundPropNF- :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack, NFData b)- => Time unit -> (a -> b) -> a -> Property-meanTimeUpperBoundPropNF upperBound run arg =- withFrozenCallStack $- checkReport upperBound $ nf run arg--checkReport- :: (KnownDivRat unit Second, KnownUnitName unit)- => HasCallStack => Time unit -> Benchmarkable -> Property-checkReport upperBound benchmarkable =- withTests 1 $ property $- evalIO runBench >>= \case- Nothing -> failedTest "Expected benchmark to complete within 120 seconds."- Just report ->- let mean = sec . realToFrac @Double @RatioNat . estPoint . anMean $ reportAnalysis report- in if mean < toUnit @Second upperBound- then succeededTest- else failedTest $- "Expected mean estimate to be under "- <> show upperBound- <> ", but was "- <> display mean- where- runBench = timeout (minute 2) $- benchmarkWith' (defaultConfig { verbosity = Quiet }) benchmarkable-- display :: Time Second -> Text- display n = case n of- (toUnit @Minute -> x) | x > minute 1 -> format x- (toUnit @Second -> x) | x > sec 1 -> format x- (toUnit @Millisecond -> x) | x > ms 1 -> format x- (toUnit @Microsecond -> x) | x > mcs 1 -> format x- (toUnit @Nanosecond -> x) | x > ns 1 -> format x- _ -> format (toUnit @Picosecond n)-- format :: forall unit. KnownUnitName unit => Time unit -> Text- format n =- toText @String $ printf "%.4f%s"- (realToFrac @RatioNat @Double $ unTime n)- (unitNameVal @unit)--------------------------------------------------------------------------------- Generator--------------------------------------------------------------------------------- | Randomly selects one of the two generators.-genEither :: MonadGen m => m a -> m b -> m (Either a b)-genEither genA genB = Gen.choice [ Left <$> genA, Right <$> genB ]---- | Generates an @a@ and a @b@ and wraps them in a tuple.-genTuple2 :: MonadGen m => m a -> m b -> m (a, b)-genTuple2 = liftA2 (,)---- | Run the given generator deterministically, by fixing its size and seed.-runGen :: HasCallStack => Range.Size -> Word64 -> Gen a -> a-runGen size seed genT =- let tree = runGenT size (Seed.from seed) genT- node = fromMaybe discardedErr $ runIdentity $ runMaybeT $ runTreeT tree- discardedErr = error $- "Generator could not produce a value for size "- <> show size <> " and seed " <> show seed- in nodeValue node----------------------------------------------------------------------------------- Roundtrip--------------------------------------------------------------------------------- | This 'TestTree' contains a property based test for conversion from--- some @x@ to some @y@ and back to @x@ (it should successfully return--- the initial @x@).-roundtripTest- :: forall x y err.- ( Show x- , Show err- , Typeable x- , Arbitrary x- , Eq x- , Eq err- )- => (x -> y)- -> (y -> Either err x)- -> TestTree-roundtripTest xToY yToX = TQC.testProperty typeName check- where- typeName = show $ typeRep (Proxy @x)- check :: x -> QC.Property- check x = yToX (xToY x) QC.=== Right x-{-# DEPRECATED roundtripTest "Use 'roundtripTree' instead." #-}---- | This 'TestTree' contains a property based test for conversion from--- some @x@ to some @y@ and back to @x@ (it should successfully return--- the initial @x@).-roundtripTree- :: forall x y err.- ( Show x- , Show y- , Show err- , Typeable x- , Eq x- , Eq err- )- => Gen x- -> (x -> y)- -> (y -> Either err x)- -> TestTree-roundtripTree genX xToY yToX = testProperty typeNameX prop- where- typeNameX = show $ typeRep (Proxy @x)- prop :: Property- prop = property $ do- x <- forAll genX- tripping x xToY yToX
src/Michelson/Text.hs view
@@ -25,7 +25,8 @@ , takeMText , dropMText , isMChar- , genMText+ , minBoundMChar+ , maxBoundMChar -- * Misc , qqMText@@ -41,9 +42,6 @@ import Data.Data (Data) import qualified Data.Text as T import Fmt (Buildable)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Quote as TH import Test.QuickCheck (Arbitrary(..), choose, listOf)@@ -128,12 +126,6 @@ dropMText :: Int -> MText -> MText dropMText n (MTextUnsafe txt) = MTextUnsafe $ T.drop n txt--genMText :: MonadGen m => m MText-genMText =- mkMTextUnsafe <$> Gen.text- (Range.linear 0 100)- (Gen.enum (toEnum @Char minBoundMChar) (toEnum @Char maxBoundMChar)) instance ToText MText where toText = unMText
src/Michelson/TypeCheck.hs view
@@ -4,8 +4,10 @@ module Michelson.TypeCheck ( typeCheckContract- , typeCheckTopLevelType- , typeVerifyTopLevelType+ , typeCheckStorage+ , typeCheckParameter+ , typeVerifyStorage+ , typeVerifyParameter , typeCheckValue , typeCheckList , typeCheckExt
src/Michelson/TypeCheck/Error.hs view
@@ -168,11 +168,11 @@ -- (e.g. @timestamp@ cannot be obtained from a `U.ValueTrue`) | NotEnoughItemsOnStack -- ^ There are not enough items on stack to perform a certain instruction.- | IllegalEntryPoint T.EpNameFromRefAnnError+ | IllegalEntrypoint T.EpNameFromRefAnnError -- ^ Invalid entrypoint name provided | UnknownContract Address -- ^ Contract with given address is not originated.- | EntryPointNotFound T.EpName+ | EntrypointNotFound T.EpName -- ^ Given entrypoint is not present. | IllegalParamDecl T.ParamEpError -- ^ Incorrect parameter declaration (with respect to entrypoints feature).@@ -222,8 +222,8 @@ "Not enough items on stack" UnknownContract addr -> "Contract is not registered: " +| addr |+ ""- IllegalEntryPoint err -> build err- EntryPointNotFound ep ->+ IllegalEntrypoint err -> build err+ EntrypointNotFound ep -> "No such entrypoint '" +| ep |+ "'" IllegalParamDecl err -> build err NegativeNat -> "Natural number cannot be negative"@@ -257,8 +257,8 @@ TCFailedOnValue v t custom ics mbTCTypeError -> "Error checking value " +| v |+ " against type "- +| toUType t |+ bool (": " +| custom |+ " ") "" (null custom)- +| (maybe "" (\e -> " " +| e |+ "") mbTCTypeError)+ +| toUType t |+ bool (": " +| custom |+ " ") "." (null custom)+ +| (maybe "" (\e -> "\n" +| e |+ ".\n") mbTCTypeError) +| buildCallStack ics TCContractError msg typeError -> "Error occured during contract typecheck: "
src/Michelson/TypeCheck/Helpers.hs view
@@ -455,17 +455,25 @@ -- | Helper function to construct instructions for binary arithmetic -- operations. arithImpl- :: ( Typeable (ArithRes aop n m ': s)+ :: forall aop inp m n s t.+ ( ArithOp aop n m+ , Typeable (ArithRes aop n m ': s) , WellTyped (ArithRes aop n m) , inp ~ (n ': m ': s)- , Monad t+ , MonadReader InstrCallStack t+ , MonadError TCError t ) => Instr inp (ArithRes aop n m ': s) -> HST inp -> VarAnn+ -> Un.ExpandedInstr -> t (SomeInstr inp)-arithImpl mkInstr i@(_ ::& _ ::& rs) vn = do- pure $ i :/ mkInstr ::: ((starNotes, Dict, vn) ::& rs)+arithImpl mkInstr i@((an, _, _) ::& (bn, _, _) ::& rs) vn uInstr = do+ case convergeArith (Proxy @aop) an bn of+ Right cn ->+ pure $ i :/ mkInstr ::: ((cn, Dict, vn) ::& rs)+ Left err -> do+ typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ AnnError err addImpl :: forall a b inp rs m.@@ -478,6 +486,7 @@ => Sing a -> Sing b -> HST inp -> VarAnn+ -> Un.ExpandedInstr -> m (SomeInstr inp) addImpl t1 t2 = case (t1, t2) of (STInt, STInt) -> arithImpl @Add ADD@@ -487,7 +496,7 @@ (STInt, STTimestamp) -> arithImpl @Add ADD (STTimestamp, STInt) -> arithImpl @Add ADD (STMutez, STMutez) -> arithImpl @Add ADD- _ -> \i vn -> typeCheckInstrErr' (Un.ADD vn) (SomeHST i) (Just ArithmeticOperation) $+ _ -> \i _ uInstr -> typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ NotNumericTypes (demote @a) (demote @b) edivImpl@@ -501,6 +510,7 @@ => Sing a -> Sing b -> HST inp -> VarAnn+ -> Un.ExpandedInstr -> m (SomeInstr inp) edivImpl t1 t2 = case (t1, t2) of (STInt, STInt) -> edivImplDo@@ -509,7 +519,7 @@ (STNat, STNat) -> edivImplDo (STMutez, STMutez) -> edivImplDo (STMutez, STNat) -> edivImplDo- _ -> \i vn -> typeCheckInstrErr' (Un.EDIV vn) (SomeHST i) (Just ArithmeticOperation) $+ _ -> \i _ uInstr -> typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ NotNumericTypes (demote @a) (demote @b) edivImplDo@@ -517,13 +527,19 @@ , WellTyped (EModOpRes n m) , WellTyped (EDivOpRes n m) , inp ~ (n ': m ': s)- , Monad t+ , MonadReader InstrCallStack t+ , MonadError TCError t ) => HST inp -> VarAnn+ -> Un.ExpandedInstr -> t (SomeInstr inp)-edivImplDo i@(_ ::& _ ::& rs) vn = do- pure $ i :/ EDIV ::: ((starNotes, Dict, vn) ::& rs)+edivImplDo i@((an, _, _) ::& (bn, _, _) ::& rs) vn uInstr = do+ case convergeEDiv an bn of+ Right cn ->+ pure $ i :/ EDIV ::: ((cn, Dict, vn) ::& rs)+ Left err -> do+ typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ AnnError err subImpl :: forall a b inp rs m.@@ -536,6 +552,7 @@ => Sing a -> Sing b -> HST inp -> VarAnn+ -> Un.ExpandedInstr -> m (SomeInstr inp) subImpl t1 t2 = case (t1, t2) of (STInt, STInt) -> arithImpl @Sub SUB@@ -545,7 +562,7 @@ (STTimestamp, STTimestamp) -> arithImpl @Sub SUB (STTimestamp, STInt) -> arithImpl @Sub SUB (STMutez, STMutez) -> arithImpl @Sub SUB- _ -> \i vn -> typeCheckInstrErr' (Un.SUB vn) (SomeHST i) (Just ArithmeticOperation) $+ _ -> \i _ uInstr -> typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ NotNumericTypes (demote @a) (demote @b) mulImpl@@ -559,6 +576,7 @@ => Sing a -> Sing b -> HST inp -> VarAnn+ -> Un.ExpandedInstr -> m (SomeInstr inp) mulImpl t1 t2 = case (t1, t2) of (STInt, STInt) -> arithImpl @Mul MUL@@ -567,7 +585,7 @@ (STNat, STNat) -> arithImpl @Mul MUL (STNat, STMutez) -> arithImpl @Mul MUL (STMutez, STNat) -> arithImpl @Mul MUL- _ -> \i vn -> typeCheckInstrErr' (Un.MUL vn) (SomeHST i) (Just ArithmeticOperation) $+ _ -> \i _ uInstr -> typeCheckInstrErr' uInstr (SomeHST i) (Just ArithmeticOperation) $ NotNumericTypes (demote @a) (demote @b) -- | Helper function to construct instructions for binary arithmetic
src/Michelson/TypeCheck/Instr.hs view
@@ -32,8 +32,10 @@ ( typeCheckContract , typeCheckValue , typeCheckList- , typeVerifyTopLevelType- , typeCheckTopLevelType+ , typeVerifyStorage+ , typeVerifyParameter+ , typeCheckStorage+ , typeCheckParameter ) where import Prelude hiding (EQ, GT, LT)@@ -60,28 +62,28 @@ import Michelson.Untyped.Annotation (VarAnn) typeCheckContract- :: TcOriginatedContracts- -> U.Contract+ :: U.Contract -> Either TCError SomeContract-typeCheckContract cs c =- runTypeCheck (U.contractParameter c) cs $ typeCheckContractImpl c+typeCheckContract c = do+ paramType <- mkSomeParamType (U.contractParameter c)+ runTypeCheck (TypeCheckContract paramType) $ typeCheckContractImpl c withWTP :: forall t a. SingI t => (WellTyped t => TypeCheck a) -> TypeCheck a withWTP fn = case getWTP @t of- Just Dict -> fn- Nothing -> throwError $ TCContractError "Not a well typed value" Nothing+ Right Dict -> fn+ Left (NotWellTyped t) -> throwError $ TCContractError ("Not a well typed value: " <> show t) Nothing withWTPInstr_ :: forall t a. SingI t => U.ExpandedInstr -> SomeHST -> (WellTyped t => TypeCheckInstr a) -> TypeCheckInstr a withWTPInstr_ v t fn = case getWTP @t of- Just Dict -> fn- Nothing -> do+ Right Dict -> fn+ Left (NotWellTyped badType) -> do loc <- ask- throwError $ TCFailedOnInstr v t loc Nothing (Just $ UnsupportedTypeForScope (demote @t) BtNotComparable)+ throwError $ TCFailedOnInstr v t loc Nothing (Just $ UnsupportedTypeForScope badType BtNotComparable) typeCheckContractImpl :: U.Contract -> TypeCheck SomeContract-typeCheckContractImpl (U.Contract (U.ParameterType mParam rootAnn) mStorage pCode) = do+typeCheckContractImpl (U.Contract (U.ParameterType mParam rootAnn) mStorage pCode entriesOrder) = do _ <- maybe (throwError $ TCContractError "no instructions in contract code" $ Just EmptyCode) pure (nonEmpty pCode) withUType mParam $ \(paramNote :: Notes param) ->@@ -113,6 +115,7 @@ { cCode = instr , cParamNotes , cStoreNotes+ , cEntriesOrder = entriesOrder } Left err -> Left $ TCContractError "contract output type violates convention:" $ Just err AnyOutInstr instr ->@@ -120,6 +123,7 @@ { cCode = instr , cParamNotes , cStoreNotes+ , cEntriesOrder = entriesOrder } where hasTypeError :: forall (t :: T) a. SingI t => Text -> BadTypeForScope -> TypeCheck a@@ -149,33 +153,52 @@ -- @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.+-- expected type @t@ and constructs @Value t@ value. -- -- If there was no match on a given pair of value and expected type, -- that is interpreted as input of wrong type and type check finishes with -- error. typeCheckValue- :: forall t.- (SingI t)+ :: forall t. SingI t => U.Value -> TypeCheckInstr (Value t)-typeCheckValue = typeCheckValImpl @t typeCheckInstr+typeCheckValue = typeCheckValImpl @t Nothing typeCheckInstr -typeVerifyTopLevelType- :: (SingI t, HasCallStack)+typeVerifyParameter+ :: SingI t => TcOriginatedContracts -> U.Value -> Either TCError (Value t)-typeVerifyTopLevelType originatedContracts valueU =- runTypeCheck param originatedContracts $ usingReaderT (def :: InstrCallStack) $- typeCheckValue valueU- where- param = error "parameter type touched during top-level type typecheck"+typeVerifyParameter originatedContracts = typeVerifyTopLevelType (Just originatedContracts) +typeVerifyStorage+ :: SingI t+ => U.Value -> Either TCError (Value t)+typeVerifyStorage = typeVerifyTopLevelType Nothing++typeVerifyTopLevelType+ :: forall t. SingI t+ => Maybe TcOriginatedContracts -> U.Value -> Either TCError (Value t)+typeVerifyTopLevelType mOriginatedContracts valueU =+ runTypeCheck (TypeCheckValue (valueU, demote @t)) $ usingReaderT (def :: InstrCallStack) $+ typeCheckValImpl mOriginatedContracts typeCheckInstr valueU++-- | Like 'typeCheckValue', but for values to be used as parameter.+--+-- Also accepts a 'TcOriginatedContracts' in order to be able to type-check+-- @contract p@ values (which can only be part of a parameter).+typeCheckParameter+ :: TcOriginatedContracts -> U.Type -> U.Value -> Either TCError SomeValue+typeCheckParameter originatedContracts = typeCheckTopLevelType (Just originatedContracts)++-- | Like 'typeCheckValue', but for values to be used as storage.+typeCheckStorage+ :: U.Type -> U.Value -> Either TCError SomeValue+typeCheckStorage = typeCheckTopLevelType Nothing+ typeCheckTopLevelType- :: HasCallStack- => TcOriginatedContracts -> U.Type -> U.Value -> Either TCError SomeValue-typeCheckTopLevelType originatedContracts typeU valueU =+ :: Maybe TcOriginatedContracts -> U.Type -> U.Value -> Either TCError SomeValue+typeCheckTopLevelType mOriginatedContracts typeU valueU = withSomeSingT (fromUType typeU) $ \(_ :: Sing t) ->- SomeValue <$> typeVerifyTopLevelType @t originatedContracts valueU+ SomeValue <$> typeVerifyTopLevelType @t mOriginatedContracts valueU -- Helper data type we use to typecheck DROPN. data TCDropHelper inp where@@ -740,19 +763,19 @@ (U.ISNAT _, SNil)-> notEnoughItemsOnStack -- Type checking is already done inside `addImpl`.- (U.ADD vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> addImpl a b inp vn+ (U.ADD vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> addImpl a b inp vn uInstr (U.ADD _, _) -> notEnoughItemsOnStack - (U.SUB vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> subImpl a b inp vn+ (U.SUB vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> subImpl a b inp vn uInstr (U.SUB _, _) -> notEnoughItemsOnStack - (U.MUL vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> mulImpl a b inp vn+ (U.MUL vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> mulImpl a b inp vn uInstr (U.MUL _, _) -> notEnoughItemsOnStack - (U.EDIV vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> edivImpl a b inp vn+ (U.EDIV vn, (a, _, _, _) ::&+ (b, _, _, _) ::&+ _) -> edivImpl a b inp vn uInstr (U.EDIV _, _) -> notEnoughItemsOnStack @@ -772,21 +795,21 @@ (U.NEG _, SNil) -> notEnoughItemsOnStack (U.LSL vn, (STNat, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @Lsl LSL inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @Lsl LSL inp vn uInstr (U.LSL _, _ ::& _ ::& _) -> failWithErr $ UnexpectedType $ (ExpectNat :| [ExpectNat]) :| [] (U.LSL _, _) -> notEnoughItemsOnStack (U.LSR vn, (STNat, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @Lsr LSR inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @Lsr LSR inp vn uInstr (U.LSR _, _ ::& _ ::& _) -> failWithErr $ UnexpectedType $ (ExpectNat :| [ExpectNat]) :| [] (U.LSR _, _) -> notEnoughItemsOnStack (U.OR vn, (STBool, _, _, _) ::&+- (STBool, _, _, _) ::&+ _) -> arithImpl @Or OR inp vn+ (STBool, _, _, _) ::&+ _) -> arithImpl @Or OR inp vn uInstr (U.OR vn, (STNat, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @Or OR inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @Or OR inp vn uInstr (U.OR _, _ ::& _ ::& _) -> failWithErr $ UnexpectedType $ (ExpectBool :| [ExpectBool]) :|@@ -795,11 +818,11 @@ (U.OR _, _) -> notEnoughItemsOnStack (U.AND vn, (STInt, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @And AND inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @And AND inp vn uInstr (U.AND vn, (STNat, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @And AND inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @And AND inp vn uInstr (U.AND vn, (STBool, _, _, _) ::&+- (STBool, _, _, _) ::&+ _) -> arithImpl @And AND inp vn+ (STBool, _, _, _) ::&+ _) -> arithImpl @And AND inp vn uInstr (U.AND _, _ ::& _ ::& _) -> failWithErr $ UnexpectedType $ (ExpectInt :| [ExpectNat]) :|@@ -809,9 +832,9 @@ (U.AND _, _) -> notEnoughItemsOnStack (U.XOR vn, (STBool, _, _, _) ::&+- (STBool, _, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn+ (STBool, _, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn uInstr (U.XOR vn, (STNat, _, _, _) ::&+- (STNat, _, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn+ (STNat, _, _, _) ::&+ _) -> arithImpl @Xor XOR inp vn uInstr (U.XOR _, _ ::& _ ::& _) -> failWithErr $ UnexpectedType $ (ExpectBool :| [ExpectBool]) :|@@ -890,33 +913,33 @@ (U.INT _, SNil) -> notEnoughItemsOnStack (U.SELF vn fn, _) -> do- (U.ParameterType cpType rootAnn) <- gets tcContractParam- -- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has- -- valid parameter type- withUType cpType $ \((`ParamNotesUnsafe` rootAnn) -> notescp :: ParamNotes t) -> do- proofScope <- onScopeCheckInstrErr @t uInstr (SomeHST inp) (Just ContractParameter)- $ checkScope @(ParameterScope t)- case proofScope of- Dict -> do- epName <- onTypeCheckInstrErr uInstr (SomeHST inp) Nothing $- epNameFromRefAnn fn `onLeft` IllegalEntryPoint- MkEntryPointCallRes (argNotes :: Notes arg) epc <-- mkEntryPointCall epName notescp- & maybeToRight (EntryPointNotFound epName)- & onTypeCheckInstrErr uInstr (SomeHST inp) Nothing-- case NTContract U.noAnn argNotes of- (ntRes :: Notes ('TContract t1)) ->- withWTPInstr @('TContract t1) $- pure $ inp :/ SELF @arg (SomeEpc epc) ::: ((ntRes, Dict, vn) ::& inp)+ mode <- gets tcMode+ case mode of+ TypeCheckValue (value, ty) ->+ tcFailedOnValue value ty "The SELF instruction cannot appear in a lambda." Nothing+ TypeCheckContract (SomeParamType _ notescp) -> do+ epName <- onTypeCheckInstrErr uInstr (SomeHST inp) Nothing $+ epNameFromRefAnn fn `onLeft` IllegalEntrypoint+ MkEntrypointCallRes (argNotes :: Notes arg) epc <-+ mkEntrypointCall epName notescp+ & maybeToRight (EntrypointNotFound epName)+ & onTypeCheckInstrErr uInstr (SomeHST inp) Nothing + case NTContract U.noAnn argNotes of+ (ntRes :: Notes ('TContract t1)) ->+ withWTPInstr @('TContract t1) $+ pure $ inp :/ SELF @arg (SomeEpc epc) ::: ((ntRes, Dict, vn) ::& inp)+ TypeCheckTest ->+ error "'SELF' appears in test typechecking."+ TypeCheckPack ->+ error "'SELF' appears in packed data." (U.CONTRACT vn fn mt, (NTAddress{}, _, _) ::& rs) -> withUType mt $ \(tns :: Notes t) -> do proofScope <- onScopeCheckInstrErr @t uInstr (SomeHST inp) (Just ContractParameter) $ checkScope @(ParameterScope t) let ns = NTOption def $ NTContract def tns epName <- onTypeCheckInstrErr uInstr (SomeHST inp) Nothing- $ epNameFromRefAnn fn `onLeft` IllegalEntryPoint+ $ epNameFromRefAnn fn `onLeft` IllegalEntrypoint case proofScope of Dict -> withWTPInstr @t $ pure $ inp :/ CONTRACT tns epName ::: ((ns, Dict, vn) ::& rs)@@ -959,13 +982,14 @@ (Contract (contr :: ContractCode p' g') paramNotes- storeNotes))+ storeNotes+ entriesOrder)) <- lift $ typeCheckContractImpl contract Refl <- onTypeCheckInstrErr uInstr (SomeHST inp) (Just ContractStorage) $ eqType @g @g' void $ onTypeCheckInstrAnnErr uInstr inp (Just ContractStorage) (converge gn storeNotes) pure- $ inp :/ CREATE_CONTRACT (Contract contr paramNotes storeNotes)+ $ inp :/ CREATE_CONTRACT (Contract contr paramNotes storeNotes entriesOrder) ::: ((starNotes, Dict, ovn) ::& (starNotes, Dict, avn) ::& rs) (U.CREATE_CONTRACT {}, _ ::& _ ::& _ ::& _) ->
src/Michelson/TypeCheck/TypeCheck.hs view
@@ -14,18 +14,25 @@ , runTypeCheckInstrIsolated , mapTCError - , tcContractParamL- , tcContractsL , tcExtFramesL+ , tcModeL+ , TypeCheckMode(..)+ , SomeParamType(..)+ , mkSomeParamType+ , mkSomeParamTypeUnsafe ) where import Control.Monad.Except (withExceptT) import Control.Monad.Reader (mapReaderT) import Data.Default (def)+import Data.Singletons (Sing)+import Fmt (Buildable, build, pretty)+import qualified Text.Show import Michelson.ErrorPos (InstrCallStack)-import Michelson.TypeCheck.Error (TCError)+import Michelson.TypeCheck.Error (TCError(..), TCTypeError(..)) import Michelson.TypeCheck.Types+import qualified Michelson.Typed as T import qualified Michelson.Untyped as U import Tezos.Address (ContractHash) import Util.Lens@@ -34,20 +41,65 @@ ExceptT TCError (State TypeCheckEnv) -type TcOriginatedContracts = Map ContractHash U.ParameterType+data SomeParamType = forall t. (T.ParameterScope t) =>+ SomeParamType (Sing t) (T.ParamNotes t) +-- | @Show@ instance of @SomeParamType@, mainly used in test.+instance Show SomeParamType where+ show = show . someParamToParameterType++-- | @Eq@ instance of @SomeParamType@, mainly used in test.+instance Eq SomeParamType where+ s1 == s2 = someParamToParameterType s1 == someParamToParameterType s2++-- | @Buildable@ instance of @SomeParamType@, mainly used in test.+instance Buildable SomeParamType where+ build = build . someParamToParameterType++-- | Helper function means to provide a quick way for creating instance+-- of @SomeParamType@ needed in test.+someParamToParameterType :: SomeParamType -> U.ParameterType+someParamToParameterType (SomeParamType s T.ParamNotesUnsafe{..}) =+ U.ParameterType (T.AsUTypeExt s pnNotes) pnRootAnn++-- | Construct @SomeParamType@ from @ParameterType@, mainly used in test.+mkSomeParamTypeUnsafe :: HasCallStack => U.ParameterType -> SomeParamType+mkSomeParamTypeUnsafe p =+ case mkSomeParamType p of+ Right sp -> sp+ Left err -> error $ "Illegal type in parameter of env contract: " <> pretty err++mkSomeParamType :: U.ParameterType -> Either TCError SomeParamType+mkSomeParamType (U.ParameterType t ann) =+ T.withUType t $ \(notescp :: T.Notes t) -> do+ case T.checkScope @(T.ParameterScope t) of+ Right T.Dict ->+ case T.mkParamNotes notescp ann of+ Right paramNotes -> Right $ SomeParamType T.sing paramNotes+ Left err ->+ Left $ TCContractError "invalid parameter declaration: " $ Just $ IllegalParamDecl err+ Left err -> Left $ TCContractError ("Parameter type is invalid: " <> pretty err) Nothing++type TcOriginatedContracts = Map ContractHash SomeParamType++-- | Typechecking mode that tells the type checker whether it is typechecking+-- contract code in actual contract, lambda, or test.+data TypeCheckMode+ = TypeCheckValue (U.Value, T.T)+ | TypeCheckContract SomeParamType+ | TypeCheckTest+ | TypeCheckPack+ -- | The typechecking state data TypeCheckEnv = TypeCheckEnv- { tcExtFrames :: ~TcExtFrames- , tcContractParam :: ~U.ParameterType- , tcContracts :: ~TcOriginatedContracts+ { tcExtFrames :: ~TcExtFrames+ , tcMode :: ~TypeCheckMode } makeLensesWith postfixLFields ''TypeCheckEnv -runTypeCheck :: U.ParameterType -> TcOriginatedContracts -> TypeCheck a -> Either TCError a-runTypeCheck param contracts act =- evaluatingState (TypeCheckEnv [] param contracts) $ runExceptT act+runTypeCheck :: TypeCheckMode -> TypeCheck a -> Either TCError a+runTypeCheck mode = evaluatingState (TypeCheckEnv [] mode) . runExceptT -- | Run type checker as if it worked isolated from other world - -- no access to environment of the current contract is allowed.@@ -58,14 +110,7 @@ -- contract which is being typechecked (because there is no contract -- that we are typechecking). runTypeCheckIsolated :: TypeCheck a -> Either TCError a-runTypeCheckIsolated = evaluatingState initSt . runExceptT- where- initSt =- TypeCheckEnv- { tcExtFrames = []- , tcContractParam = error "Contract param touched"- , tcContracts = mempty- }+runTypeCheckIsolated = runTypeCheck TypeCheckTest type TcResult inp = Either TCError (SomeInstr inp) @@ -87,7 +132,7 @@ -- have to be refactored too. -- Therefore, I am using ReaderT over TypeCheck. type TcInstrHandler- = forall inp. Typeable inp+ = forall inp. (Typeable inp, HasCallStack) => U.ExpandedInstr -> HST inp -> TypeCheckInstr (SomeInstr inp)
src/Michelson/TypeCheck/Types.hs view
@@ -12,15 +12,17 @@ , SomeContract (..) , BoundVars (..) , TcExtFrames+ , NotWellTyped (..) , getWTP , withWTPm+ , unsafeWithWTP , mapSomeContract , noBoundVars ) where import Data.Constraint (Dict(..)) import qualified Data.Map.Lazy as Map-import Data.Singletons (Sing, SingI(..))+import Data.Singletons (Sing, SingI(..), demote) import Fmt (Buildable(..), Builder, (+|), (|+)) import Prelude hiding (EQ, GT, LT) import qualified Text.Show@@ -187,51 +189,68 @@ -- | State for type checking @nop@ type TcExtFrames = [BoundVars] -fromMDict :: Maybe (Dict a) -> (a => Maybe (Dict b)) -> Maybe (Dict b)-fromMDict ma b = ma >>= (\Dict -> b)+-- | Error type for when a value is not well-typed.+data NotWellTyped = NotWellTyped T +fromEDict :: Either e (Dict a) -> (a => Either e (Dict b)) -> Either e (Dict b)+fromEDict ma b = ma >>= (\Dict -> b)+ -- | Given a type, provide evidence that it is well typed w.r.t to the -- Michelson rules regarding where comparable types are required.-getWTP :: forall t. SingI t => Maybe (Dict (WellTyped t))+getWTP :: forall t. (SingI t) => Either NotWellTyped (Dict (WellTyped t)) getWTP = case sing @t of- STKey -> Just Dict- STUnit -> Just Dict- STSignature -> Just Dict- STChainId -> Just Dict- STOption s -> fromMDict (getWTP_ s) (Just Dict)- STList s -> fromMDict (getWTP_ s) (Just Dict)- STSet s -> fromMDict (getWTP_ s) $ fromMDict (T.getComparableProofS s) (Just Dict)- STOperation -> Just Dict- STContract s -> fromMDict (getWTP_ s) (Just Dict)- STPair s1 s2 -> fromMDict (getWTP_ s1) $ fromMDict (getWTP_ s2) $ Just Dict- STOr s1 s2 -> fromMDict (getWTP_ s1) $ fromMDict (getWTP_ s2) $ Just Dict- STLambda s1 s2 -> fromMDict (getWTP_ s1) $ fromMDict (getWTP_ s2) $ Just Dict- STMap s1 s2 ->- fromMDict (getWTP_ s1) $- fromMDict (getWTP_ s2) $- fromMDict (T.getComparableProofS s1) $ Just Dict- STBigMap s1 s2 ->- fromMDict (getWTP_ s1) $- fromMDict (getWTP_ s2) $- fromMDict (T.getComparableProofS s1) $ Just Dict- STInt -> Just Dict- STNat -> Just Dict- STString -> Just Dict- STBytes -> Just Dict- STMutez -> Just Dict- STBool -> Just Dict- STKeyHash -> Just Dict- STTimestamp -> Just Dict- STAddress -> Just Dict+ STKey -> Right Dict+ STUnit -> Right Dict+ STSignature -> Right Dict+ STChainId -> Right Dict+ STOption s -> fromEDict (getWTP_ s) (Right Dict)+ STList s -> fromEDict (getWTP_ s) (Right Dict)+ STSet (s :: Sing si) ->+ fromEDict (getWTP_ s) $+ fromEDict+ ( maybeToRight (NotWellTyped $ demote @si) $ T.getComparableProofS s+ ) $ Right Dict+ STOperation -> Right Dict+ STContract s -> fromEDict (getWTP_ s) (Right Dict)+ STPair s1 s2 -> fromEDict (getWTP_ s1) $ fromEDict (getWTP_ s2) $ Right Dict+ STOr s1 s2 -> fromEDict (getWTP_ s1) $ fromEDict (getWTP_ s2) $ Right Dict+ STLambda s1 s2 -> fromEDict (getWTP_ s1) $ fromEDict (getWTP_ s2) $ Right Dict+ STMap (s1 :: Sing si) s2 ->+ fromEDict (getWTP_ s1) $+ fromEDict (getWTP_ s2) $+ fromEDict+ ( maybeToRight (NotWellTyped $ demote @si) $ T.getComparableProofS s1+ ) $ Right Dict+ STBigMap (s1 :: Sing si) s2 ->+ fromEDict (getWTP_ s1) $+ fromEDict (getWTP_ s2) $+ fromEDict+ ( maybeToRight (NotWellTyped $ demote @si) $ T.getComparableProofS s1+ ) $ Right Dict+ STInt -> Right Dict+ STNat -> Right Dict+ STString -> Right Dict+ STBytes -> Right Dict+ STMutez -> Right Dict+ STBool -> Right Dict+ STKeyHash -> Right Dict+ STTimestamp -> Right Dict+ STAddress -> Right Dict where- getWTP_ :: forall t1. SingI t1 => Sing t1 -> Maybe (Dict (WellTyped t1))+ getWTP_ :: forall t1. SingI t1 => Sing t1 -> Either NotWellTyped (Dict (WellTyped t1)) getWTP_ _ = getWTP @t1 -- | Given a type and an action that requires evidence that the type is well typed, -- generate the evidence and execute the action, or else fail with an error. withWTPm :: forall t m a. (MonadFail m, SingI t) => (WellTyped t => m a) -> m a withWTPm a = case getWTP @t of- Just Dict -> a- Nothing ->- fail ("This type is not well typed because it has an non-comparable type in it," <>- "where a comparable type is required")+ Right Dict -> a+ Left (NotWellTyped t) ->+ fail ("This type is not well typed because it has an non-comparable type in it: '" <> (show t) <>+ "', where a comparable type is required")++-- | Similar to @withWTPm@ but is mean to be used within tests.+unsafeWithWTP :: forall t a. SingI t => (WellTyped t => a) -> a+unsafeWithWTP fn = case getWTP @t of+ Right Dict -> fn+ Left (NotWellTyped t) -> error $ "Type is not well typed: " <> (show t)
src/Michelson/TypeCheck/Value.hs view
@@ -4,6 +4,7 @@ module Michelson.TypeCheck.Value ( typeCheckValImpl+ , tcFailedOnValue ) where import Control.Monad.Except (liftEither, throwError)@@ -18,16 +19,16 @@ import Michelson.Text import Michelson.TypeCheck.Error (TCError(..), TCTypeError(..)) import Michelson.TypeCheck.Helpers-import Michelson.TypeCheck.TypeCheck (TcInstrHandler, TypeCheckEnv(..), TypeCheckInstr)+import Michelson.TypeCheck.TypeCheck (SomeParamType(..), TcInstrHandler, TcOriginatedContracts, TypeCheckInstr) import Michelson.TypeCheck.Types import Michelson.Typed- (pattern AsUTypeExt, EpAddress(..), KnownT, Notes(..), ParamNotes(..), SingT(..), Value'(..),+ (EpAddress(..), KnownT, Notes(..), SingT(..), Value'(..), fromSingT, starNotes) import qualified Michelson.Typed as T import qualified Michelson.Untyped as U import Tezos.Address (Address(..)) import Tezos.Core-import Tezos.Crypto (parseKeyHash, parsePublicKey, parseSignature)+import Tezos.Crypto tcFailedOnValue :: U.Value -> T.T -> Text -> Maybe TCTypeError -> TypeCheckInstr a tcFailedOnValue v t msg err = do@@ -46,167 +47,210 @@ -- If there was no match on a given pair of value and expected type, -- that is interpreted as input of wrong type and type check finishes with -- error.+--+-- @typeCheckValImpl@ also has a 'Maybe TcOriginatedContracts' argument that+-- should contain the originated contracts when typechecking a parameter and+-- 'Nothing' otherwise. typeCheckValImpl :: forall ty. SingI ty- => TcInstrHandler+ => Maybe TcOriginatedContracts+ -> TcInstrHandler -> U.Value -> TypeCheckInstr (T.Value ty)-typeCheckValImpl tcDo uvalue = case (uvalue, sing @ty) of- (U.ValueInt i, STInt) -> pure $ T.VInt i- (v@(U.ValueInt i), t@STNat)- | i >= 0 -> pure $ VNat (fromInteger i)- | otherwise -> tcFailedOnValue v (fromSingT t) "" (Just NegativeNat)- (v@(U.ValueInt i), t@STMutez) -> do- case mkMutez $ fromInteger i of- Just mtz -> pure $ VMutez mtz- Nothing -> tcFailedOnValue v (fromSingT t) "" (Just InvalidTimestamp)- (U.ValueString s, STString) ->- pure $ VString s- (v@(U.ValueString s), t@STAddress) -> do- case T.parseEpAddress (unMText s) of- Right addr -> pure $ VAddress addr- Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidAddress err)- (v@(U.ValueString s), t@STKeyHash) -> do- case parseKeyHash (unMText s) of- Right kHash -> pure $ VKeyHash kHash- Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidKeyHash err)- (v@(U.ValueString s), t@STTimestamp) -> do- case parseTimestamp $ unMText s of- Just tstamp -> pure $ VTimestamp tstamp- Nothing -> tcFailedOnValue v (fromSingT t) "" (Just InvalidTimestamp)- (U.ValueInt i, STTimestamp) ->- pure $ VTimestamp (timestampFromSeconds i)- (U.ValueBytes (U.InternalByteString s), STBytes) ->- pure $ VBytes s- (U.ValueTrue, STBool) -> pure $ VBool True- (U.ValueFalse, STBool) -> pure $ VBool False+typeCheckValImpl mOriginatedContracts tcDo = doTypeCheckVal+ where+ doTypeCheckVal :: forall tz. SingI tz => U.Value -> TypeCheckInstr (T.Value tz)+ doTypeCheckVal uvalue = case (uvalue, sing @tz) of+ (U.ValueInt i, STInt) -> pure $ T.VInt i+ (v@(U.ValueInt i), t@STNat)+ | i >= 0 -> pure $ VNat (fromInteger i)+ | otherwise -> tcFailedOnValue v (fromSingT t) "" (Just NegativeNat)+ (v@(U.ValueInt i), t@STMutez) -> case mkMutez $ fromInteger i of+ Just mtz -> pure $ VMutez mtz+ Nothing -> tcFailedOnValue v (fromSingT t) "" (Just InvalidTimestamp)+ (U.ValueString s, STString) -> pure $ VString s+ (v@(U.ValueString s), t@STAddress) -> case T.parseEpAddress (unMText s) of+ Right addr -> pure $ VAddress addr+ Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidAddress err)+ (v@(U.ValueBytes b), t@STAddress) -> case T.parseEpAddressRaw (U.unInternalByteString b) of+ Right addr -> pure $ VAddress addr+ Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidAddress err)+ (v@(U.ValueString s), t@STKeyHash) -> case parseKeyHash (unMText s) of+ Right kHash -> pure $ VKeyHash kHash+ Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidKeyHash err)+ (v@(U.ValueBytes b), t@STKeyHash) ->+ case parseKeyHashRaw (U.unInternalByteString b) of+ Right kHash -> pure $ VKeyHash kHash+ Left err -> tcFailedOnValue v (fromSingT t) "" (Just $ InvalidKeyHash err)+ (v@(U.ValueString s), t@STTimestamp) -> case parseTimestamp $ unMText s of+ Just tstamp -> pure $ VTimestamp tstamp+ Nothing -> tcFailedOnValue v (fromSingT t) "" (Just InvalidTimestamp)+ (U.ValueInt i, STTimestamp) -> pure $ VTimestamp (timestampFromSeconds i)+ (U.ValueBytes (U.InternalByteString s), STBytes) -> pure $ VBytes s+ (U.ValueTrue, STBool) -> pure $ VBool True+ (U.ValueFalse, STBool) -> pure $ VBool False - (U.ValueString (parsePublicKey . unMText -> Right s), STKey) ->- pure $ T.VKey s+ (U.ValueString (parsePublicKey . unMText -> Right s), STKey) ->+ pure $ T.VKey s+ (U.ValueBytes (parsePublicKeyRaw . U.unInternalByteString -> Right s), STKey) ->+ pure $ VKey s - (U.ValueString (parseSignature . unMText -> Right s), STSignature) ->- pure $ VSignature s+ (U.ValueString (parseSignature . unMText -> Right s), STSignature) ->+ pure $ VSignature s+ (U.ValueBytes (parseSignatureRaw . U.unInternalByteString -> Right s), STSignature) ->+ pure $ VSignature s - (U.ValueString (parseChainId . unMText -> Right ci), STChainId) ->- pure $ VChainId ci- (U.ValueBytes (mkChainId . U.unInternalByteString -> Just ci), STChainId) ->- pure $ VChainId ci+ (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)) -> do- instrPos <- ask- contracts <- gets tcContracts- let ensureTypeMatches :: forall t'. KnownT t' => TypeCheckInstr (cp :~: t')- ensureTypeMatches =- liftEither @_ @TypeCheckInstr $- first (TCFailedOnValue cv (demote @ty) "wrong contract parameter" instrPos . Just) $- eqType @cp @t'- let unsupportedType :: T.BadTypeForScope -> TCError- unsupportedType reason =- TCFailedOnValue cv (fromSingT pc) ("Unsupported type in type argument of 'contract' type") instrPos $- Just $ UnsupportedTypeForScope (fromSingT pc) reason- let EpAddress addr epName = epAddr- case addr of- KeyAddress _ -> do- Refl <- ensureTypeMatches @'T.TUnit- pure $ VContract addr T.sepcPrimitive- ContractAddress ca ->- case M.lookup ca contracts of- Just (U.ParameterType (AsUTypeExt (_ :: Sing cp') cpNotes) rootAnn) -> do- -- Wrapping into 'ParamNotesUnsafe' is safe because originated contract has- -- valid parameter type- let paramNotes = ParamNotesUnsafe { pnNotes = cpNotes, pnRootAnn = rootAnn }- Dict <- liftEither $ T.checkScope @(T.ParameterScope cp')- `onLeft` unsupportedType- case T.mkEntryPointCall epName paramNotes of- Nothing ->- throwError $- TCFailedOnValue cv (demote @ty) "unknown entrypoint" instrPos . Just $- EntryPointNotFound epName- Just (T.MkEntryPointCallRes (_ :: Notes t') epc) -> do- Refl <- ensureTypeMatches @t'- pure $ VContract addr (T.SomeEpc epc)- Nothing ->- throwError $ TCFailedOnValue cv (demote @ty) "Contract literal unknown"- instrPos (Just $ UnknownContract addr)+ (cv@(U.ValueString (T.parseEpAddress . unMText -> Right addr))+ , STContract pc) -> typecheckContractValue cv addr pc+ (cv@(U.ValueBytes (T.parseEpAddressRaw . U.unInternalByteString -> Right addr))+ , STContract pc) -> typecheckContractValue cv addr pc - (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.ValueUnit, STUnit) -> pure $ VUnit+ (U.ValuePair ml mr, STPair _ _) -> do+ l <- doTypeCheckVal ml+ r <- doTypeCheckVal mr+ pure $ VPair (l, r)+ (U.ValueLeft ml, STOr{}) -> do+ l <- doTypeCheckVal ml+ pure $ VOr (Left l)+ (U.ValueRight mr, STOr{}) -> do+ r <- doTypeCheckVal mr+ pure $ VOr (Right r)+ (U.ValueSome mv, STOption{}) -> do+ v <- doTypeCheckVal mv+ pure $ VOption (Just v)+ (U.ValueNone, STOption _) -> do+ pure $ VOption Nothing - (U.ValueNil, STList _) ->- pure $ T.VList []+ (U.ValueNil, STList _) ->+ pure $ T.VList [] - (U.ValueSeq (toList -> mels), STList _) -> do- els <- typeCheckValsImpl tcDo mels- pure $ VList els+ (U.ValueSeq (toList -> mels), STList _) -> do+ els <- typeCheckValsImpl mels+ pure $ VList els - (U.ValueNil, STSet (s :: Sing st)) -> do- instrPos <- ask- case T.getComparableProofS s of- Just Dict -> pure (T.VSet S.empty)- Nothing -> throwError $ TCFailedOnValue uvalue (demote @st) "Non comparable types are not allowed in Sets"- instrPos (Just $ UnsupportedTypeForScope (demote @st) T.BtNotComparable)+ (U.ValueNil, STSet (s :: Sing st)) -> do+ instrPos <- ask+ case T.getComparableProofS s of+ Just Dict -> pure (T.VSet S.empty)+ Nothing -> throwError $ TCFailedOnValue uvalue (demote @st) "Non comparable types are not allowed in Sets"+ instrPos (Just $ UnsupportedTypeForScope (demote @st) T.BtNotComparable) - (sq@(U.ValueSeq (toList -> mels)), s@(STSet (vt :: Sing st))) -> withComparable vt sq s $ do- instrPos <- ask+ (sq@(U.ValueSeq (toList -> mels)), s@(STSet (vt :: Sing st))) -> withComparable vt sq s $ do+ instrPos <- ask - els <- typeCheckValsImpl tcDo mels- elsS <- liftEither $ S.fromDistinctAscList <$> ensureDistinctAsc id els- `onLeft` \msg -> TCFailedOnValue sq (fromSingT vt) msg instrPos Nothing- pure $ VSet elsS+ els <- typeCheckValsImpl mels+ elsS <- liftEither $ S.fromDistinctAscList <$> ensureDistinctAsc id els+ `onLeft` \msg -> TCFailedOnValue sq (fromSingT vt) msg instrPos Nothing+ pure $ VSet elsS - (v@U.ValueNil, s@(STMap (st :: Sing st) _)) -> withComparable st v s $ pure $ T.VMap M.empty+ (v@U.ValueNil, s@(STMap (st :: Sing st) _)) -> withComparable st v s $ pure $ T.VMap M.empty - (sq@(U.ValueMap (toList -> mels)), s@(STMap (kt :: Sing st) _)) -> withComparable kt sq s $ do- keyOrderedElts <- typeCheckMapVal tcDo mels sq kt- pure $ VMap (M.fromDistinctAscList keyOrderedElts)+ (sq@(U.ValueMap (toList -> mels)), s@(STMap (kt :: Sing st) _)) -> withComparable kt sq s $ do+ keyOrderedElts <- typeCheckMapVal mels sq kt+ pure $ VMap (M.fromDistinctAscList keyOrderedElts) - (v@U.ValueNil, s@(STBigMap (st :: Sing st) _)) ->- withComparable st v s $ pure $ T.VBigMap M.empty+ (v@U.ValueNil, s@(STBigMap (st :: Sing st) _)) ->+ withComparable st v s $ pure $ T.VBigMap M.empty - (sq@(U.ValueMap (toList -> mels)), s@(STBigMap (kt :: Sing st) _)) -> withComparable kt sq s $ do- keyOrderedElts <- typeCheckMapVal tcDo mels sq kt- pure $ VBigMap (M.fromDistinctAscList keyOrderedElts)+ (sq@(U.ValueMap (toList -> mels)), s@(STBigMap (kt :: Sing st) _)) -> withComparable kt sq s $ do+ keyOrderedElts <- typeCheckMapVal mels sq kt+ pure $ VBigMap (M.fromDistinctAscList keyOrderedElts) - (v, STLambda (_ :: Sing it) (_ :: Sing ot)) -> do- mp <- case v of- U.ValueNil -> pure []- U.ValueLambda mp -> pure $ toList mp- _ -> tcFailedOnValue v (demote @ty) "unexpected value" Nothing- _ :/ instr <- withWTP @it $ typeCheckImpl tcDo mp ((starNotes @it, Dict, def) ::& SNil)- case instr of- lam ::: (lo :: HST lo) -> withWTP @ot $ do- case eqHST1 @ot lo of- Right Refl -> do- pure $ VLam (T.RfNormal lam)- Left m ->- tcFailedOnValue v (demote @ty)- "wrong output type of lambda's value:" (Just m)- AnyOutInstr lam ->- pure $ VLam (T.RfAlwaysFails lam)+ (v, STLambda (_ :: Sing it) (_ :: Sing ot)) -> do+ mp <- case v of+ U.ValueNil -> pure []+ U.ValueLambda mp -> pure $ toList mp+ _ -> tcFailedOnValue v (demote @ty) "unexpected value" Nothing+ _ :/ instr <- withWTP @it uvalue $ typeCheckImpl tcDo mp ((starNotes @it, Dict, def) ::& SNil)+ case instr of+ lam ::: (lo :: HST lo) -> withWTP @ot uvalue $ do+ case eqHST1 @ot lo of+ Right Refl -> do+ pure $ VLam (T.RfNormal lam)+ Left m ->+ tcFailedOnValue v (demote @ty)+ "wrong output type of lambda's value:" (Just m)+ AnyOutInstr lam ->+ pure $ VLam (T.RfAlwaysFails lam) - (v, t) -> tcFailedOnValue v (fromSingT t) "unknown value" Nothing- where- withWTP :: forall t a. SingI t => (T.WellTyped t => TypeCheckInstr a) -> TypeCheckInstr a- withWTP fn = case getWTP @t of- Just Dict -> fn- Nothing -> tcFailedOnValue uvalue (fromSingT $ sing @ty) "Value is not well typed" Nothing+ (v, t) -> tcFailedOnValue v (fromSingT t) "unknown value" Nothing + withWTP+ :: forall t a. SingI t+ => U.Value+ -> (T.WellTyped t => TypeCheckInstr a)+ -> TypeCheckInstr a+ withWTP uvalue fn = case getWTP @t of+ Right Dict -> fn+ Left (NotWellTyped t) -> tcFailedOnValue uvalue (fromSingT $ sing @ty)+ ("Value is not well typed: " <> show t) Nothing++ typeCheckValsImpl+ :: forall t . (SingI t)+ => [U.Value]+ -> TypeCheckInstr [T.Value t]+ typeCheckValsImpl mvs =+ fmap reverse $ foldM (\res mv -> (: res) <$> doTypeCheckVal @t mv) [] mvs++ -- Typechecks given list of @Elt@s and ensures that its keys are in ascending order.+ --+ -- It return list of pairs (key, value) with keys in ascending order+ -- so it is safe to call @fromDistinctAscList@ on returned list+ typeCheckMapVal+ :: forall kt vt. (SingI kt, SingI vt)+ => [U.Elt U.ExpandedOp]+ -> U.Value+ -> Sing kt+ -> TypeCheckInstr [(T.Value kt, T.Value vt)]+ typeCheckMapVal mels sq kt = withComparable kt sq kt $ do+ instrPos <- ask+ ks <- typeCheckValsImpl @kt (map (\(U.Elt k _) -> k) mels)+ vals <- typeCheckValsImpl @vt (map (\(U.Elt _ v) -> v) mels)+ ksS <- liftEither $ ensureDistinctAsc id ks+ `onLeft` \msg -> TCFailedOnValue sq (fromSingT kt) msg instrPos Nothing+ pure $ zip ksS vals++ typecheckContractValue+ :: forall cp tz. (Typeable cp, SingI cp, tz ~ 'T.TContract cp)+ => U.Value -> EpAddress -> Sing cp -> TypeCheckInstr (T.Value tz)+ typecheckContractValue cv (EpAddress addr epName) pc = do++ instrPos <- ask+ let ensureTypeMatches :: forall t'. KnownT t' => TypeCheckInstr (cp :~: t')+ ensureTypeMatches = liftEither @_ @TypeCheckInstr $+ first (TCFailedOnValue cv (demote @ty) "wrong contract parameter" instrPos . Just) $+ eqType @cp @t'+ let unsupportedType :: T.BadTypeForScope -> TCError+ unsupportedType reason = TCFailedOnValue cv (fromSingT pc)+ "Unsupported type in type argument of 'contract' type" instrPos $+ Just $ UnsupportedTypeForScope (fromSingT pc) reason+ case addr of+ KeyAddress _ -> do+ Refl <- ensureTypeMatches @'T.TUnit+ pure $ VContract addr T.sepcPrimitive+ ContractAddress ca -> case mOriginatedContracts of+ Nothing -> liftEither . Left $ unsupportedType T.BtHasContract+ Just originatedContracts -> case M.lookup ca originatedContracts of+ Just (SomeParamType (_ :: Sing cp') paramNotes) ->+ case T.mkEntrypointCall epName paramNotes of+ Nothing ->+ throwError $+ TCFailedOnValue cv (demote @ty) "unknown entrypoint" instrPos . Just $+ EntrypointNotFound epName+ Just (T.MkEntrypointCallRes (_ :: Notes t') epc) -> do+ Refl <- ensureTypeMatches @t'+ pure $ VContract addr (T.SomeEpc epc)+ Nothing ->+ throwError $ TCFailedOnValue cv (demote @ty) "Contract literal unknown"+ instrPos (Just $ UnknownContract addr)+ withComparable :: forall a (t :: T.T) ty. Sing a -> U.Value@@ -217,37 +261,5 @@ Just Dict -> act Nothing -> do instrPos <- ask- liftEither $ Left $ TCFailedOnValue uv (fromSingT t) "Require a comparable type here" instrPos Nothing---- | Function @typeCheckMapVal@ typechecks given list of @Elt@s and--- ensures, that its keys are in ascending order.------ It return list of pairs (key, value) with keys in ascending order--- so it is safe to call @fromDistinctAscList@ on returned list-typeCheckMapVal- :: forall kt vt. (SingI kt, SingI vt)- => TcInstrHandler- -> [U.Elt U.ExpandedOp]- -> U.Value- -> Sing kt- -> TypeCheckInstr [(T.Value kt, T.Value vt)]-typeCheckMapVal tcDo mels sq kt = withComparable kt sq kt $ do- instrPos <- ask- ks <- typeCheckValsImpl @kt tcDo (map (\(U.Elt k _) -> k) mels)- vals <- typeCheckValsImpl @vt tcDo (map (\(U.Elt _ v) -> v) mels)- ksS <- liftEither $ ensureDistinctAsc id ks- `onLeft` \msg -> TCFailedOnValue sq (fromSingT kt) msg instrPos Nothing- pure $ zip ksS vals--typeCheckValsImpl- :: forall t . (SingI t)- => TcInstrHandler- -> [U.Value]- -> TypeCheckInstr [T.Value t]-typeCheckValsImpl tcDo mvs =- fmap reverse $ foldM check [] mvs- where- check :: [T.Value t] -> U.Value -> TypeCheckInstr [T.Value t]- check res mv = do- v <- typeCheckValImpl @t tcDo mv- pure (v : res)+ liftEither . Left $+ TCFailedOnValue uv (fromSingT t) "Require a comparable type here" instrPos Nothing
src/Michelson/Typed.hs view
@@ -11,7 +11,7 @@ import Michelson.Typed.Arith as Exports import Michelson.Typed.Convert as Exports import Michelson.Typed.Doc as Exports-import Michelson.Typed.EntryPoints as Exports+import Michelson.Typed.Entrypoints as Exports import Michelson.Typed.Extract as Exports import Michelson.Typed.Haskell as Exports import Michelson.Typed.Instr as Exports
src/Michelson/Typed/Arith.hs view
@@ -37,6 +37,7 @@ import Data.Singletons (Sing, SingI(..)) import Fmt (Buildable(build)) +import Michelson.Typed.Annotation (AnnConvergeError, Notes(..), converge, convergeAnns, starNotes) import Michelson.Typed.Sing (SingT(..)) import Michelson.Typed.T (T(..)) import Michelson.Typed.Value (Comparable, Comparability(..), checkComparability, Value'(..))@@ -56,6 +57,13 @@ -- @ArithRes Add CNat CInt = CInt@. type ArithRes aop n m :: T + -- | Converge the notes of given operands.+ convergeArith+ :: proxy aop+ -> Notes n+ -> Notes m+ -> Either AnnConvergeError (Notes (ArithRes aop n m))+ -- | Evaluate arithmetic operation on given operands. evalOp :: proxy aop@@ -110,75 +118,96 @@ instance ArithOp Add 'TNat 'TInt where type ArithRes Add 'TNat 'TInt = 'TInt+ convergeArith _ _ n2 = Right n2 evalOp _ (VNat i) (VInt j) = Right $ VInt (toInteger i + j) instance ArithOp Add 'TInt 'TNat where type ArithRes Add 'TInt 'TNat = 'TInt+ convergeArith _ n1 _ = Right n1 evalOp _ (VInt i) (VNat j) = Right $ VInt (i + toInteger j) instance ArithOp Add 'TNat 'TNat where type ArithRes Add 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VNat (i + j) instance ArithOp Add 'TInt 'TInt where type ArithRes Add 'TInt 'TInt = 'TInt+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VInt i) (VInt j) = Right $ VInt (i + j) instance ArithOp Add 'TTimestamp 'TInt where type ArithRes Add 'TTimestamp 'TInt = 'TTimestamp+ convergeArith _ n1 _ = Right n1 evalOp _ (VTimestamp i) (VInt j) = Right $ VTimestamp $ timestampFromSeconds $ timestampToSeconds i + j instance ArithOp Add 'TInt 'TTimestamp where type ArithRes Add 'TInt 'TTimestamp = 'TTimestamp+ convergeArith _ _ n2 = Right n2 evalOp _ (VInt i) (VTimestamp j) = Right $ VTimestamp $ timestampFromSeconds $ timestampToSeconds j + i instance ArithOp Add 'TMutez 'TMutez where type ArithRes Add 'TMutez 'TMutez = 'TMutez+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ n@(VMutez i) m@(VMutez j) = res where res = maybe (Left $ MutezArithError AddOverflow n m) (Right . VMutez) $ i `addMutez` j instance ArithOp Sub 'TNat 'TInt where type ArithRes Sub 'TNat 'TInt = 'TInt+ convergeArith _ _ n2 = Right n2 evalOp _ (VNat i) (VInt j) = Right $ VInt (toInteger i - j) instance ArithOp Sub 'TInt 'TNat where type ArithRes Sub 'TInt 'TNat = 'TInt+ convergeArith _ n1 _ = Right n1 evalOp _ (VInt i) (VNat j) = Right $ VInt (i - toInteger j) instance ArithOp Sub 'TNat 'TNat where type ArithRes Sub 'TNat 'TNat = 'TInt+ -- | Subtraction between @Nat@ and @Nat@ does not retain annotation.+ convergeArith _ n1 n2 = (const starNotes) <$> converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VInt (toInteger i - toInteger j) instance ArithOp Sub 'TInt 'TInt where type ArithRes Sub 'TInt 'TInt = 'TInt+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VInt i) (VInt j) = Right $ VInt (i - j) instance ArithOp Sub 'TTimestamp 'TInt where type ArithRes Sub 'TTimestamp 'TInt = 'TTimestamp+ convergeArith _ n1 _ = Right n1 evalOp _ (VTimestamp i) (VInt j) = Right $ VTimestamp $ timestampFromSeconds $ timestampToSeconds i - j instance ArithOp Sub 'TTimestamp 'TTimestamp where type ArithRes Sub 'TTimestamp 'TTimestamp = 'TInt+ convergeArith _ (NTTimestamp a) (NTTimestamp b) = NTInt <$> (convergeAnns a b) evalOp _ (VTimestamp i) (VTimestamp j) = Right $ VInt $ timestampToSeconds i - timestampToSeconds j instance ArithOp Sub 'TMutez 'TMutez where type ArithRes Sub 'TMutez 'TMutez = 'TMutez+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ n@(VMutez i) m@(VMutez j) = res where res = maybe (Left $ MutezArithError SubUnderflow n m) (Right . VMutez) $ i `subMutez` j instance ArithOp Mul 'TNat 'TInt where type ArithRes Mul 'TNat 'TInt = 'TInt+ convergeArith _ _ n2 = Right n2 evalOp _ (VNat i) (VInt j) = Right $ VInt (toInteger i * j) instance ArithOp Mul 'TInt 'TNat where type ArithRes Mul 'TInt 'TNat = 'TInt+ convergeArith _ n1 _ = Right n1 evalOp _ (VInt i) (VNat j) = Right $ VInt (i * toInteger j) instance ArithOp Mul 'TNat 'TNat where type ArithRes Mul 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VNat (i * j) instance ArithOp Mul 'TInt 'TInt where type ArithRes Mul 'TInt 'TInt = 'TInt+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VInt i) (VInt j) = Right $ VInt (i * j) instance ArithOp Mul 'TNat 'TMutez where type ArithRes Mul 'TNat 'TMutez = 'TMutez+ convergeArith _ _ n2 = Right n2 evalOp _ n@(VNat i) m@(VMutez j) = res where res = maybe (Left $ MutezArithError MulOverflow n m) (Right . VMutez) $ j `mulMutez` i instance ArithOp Mul 'TMutez 'TNat where type ArithRes Mul 'TMutez 'TNat = 'TMutez+ convergeArith _ n1 _ = Right n1 evalOp _ n@(VMutez i) m@(VNat j) = res where res = maybe (Left $ MutezArithError MulOverflow n m) (Right . VMutez) $ i `mulMutez` j@@ -196,30 +225,38 @@ instance ArithOp Or 'TNat 'TNat where type ArithRes Or 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VNat (i .|. j) instance ArithOp Or 'TBool 'TBool where type ArithRes Or 'TBool 'TBool = 'TBool+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VBool i) (VBool j) = Right $ VBool (i .|. j) instance ArithOp And 'TInt 'TNat where type ArithRes And 'TInt 'TNat = 'TNat+ convergeArith _ _ n2 = Right n2 evalOp _ (VInt i) (VNat j) = Right $ VNat ((fromInteger i) .&. j) instance ArithOp And 'TNat 'TNat where type ArithRes And 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VNat (i .&. j) instance ArithOp And 'TBool 'TBool where type ArithRes And 'TBool 'TBool = 'TBool+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VBool i) (VBool j) = Right $ VBool (i .&. j) instance ArithOp Xor 'TNat 'TNat where type ArithRes Xor 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VNat i) (VNat j) = Right $ VNat (i `xor` j) instance ArithOp Xor 'TBool 'TBool where type ArithRes Xor 'TBool 'TBool = 'TBool+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ (VBool i) (VBool j) = Right $ VBool (i `xor` j) instance ArithOp Lsl 'TNat 'TNat where type ArithRes Lsl 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ n@(VNat i) m@(VNat j) = if j > 256 then Left $ ShiftArithError LslOverflow n m@@ -227,6 +264,7 @@ instance ArithOp Lsr 'TNat 'TNat where type ArithRes Lsr 'TNat 'TNat = 'TNat+ convergeArith _ n1 n2 = converge n1 n2 evalOp _ n@(VNat i) m@(VNat j) = if j > 256 then Left $ ShiftArithError LsrUnderflow n m
src/Michelson/Typed/Convert.hs view
@@ -17,7 +17,7 @@ import Michelson.Text import Michelson.Typed.Annotation (Notes(..))-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Extract (mkUType, toUType) import Michelson.Typed.Instr as Instr import Michelson.Typed.Scope@@ -38,6 +38,7 @@ { contractParameter = U.ParameterType (untypeDemoteT @param) U.noAnn , contractStorage = untypeDemoteT @store , contractCode = instrToOps contract+ , entriesOrder = U.canonicalEntriesOrder } convertContract@@ -48,6 +49,7 @@ in c { U.contractParameter = U.ParameterType (mkUType (pnNotes $ cParamNotes fc)) (pnRootAnn (cParamNotes fc)) , U.contractStorage = mkUType (cStoreNotes fc)+ , U.entriesOrder = cEntriesOrder fc } -- | Convert a typed 'Val' to an untyped 'Value'.
− src/Michelson/Typed/EntryPoints.hs
@@ -1,478 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Utilities for lightweight entrypoints support.-module Michelson.Typed.EntryPoints- ( EpAddress (..)- , ParseEpAddressError (..)- , formatEpAddress- , mformatEpAddress- , parseEpAddress- , unsafeParseEpAddress- , genEpAddress-- , ParamNotes (..)- , pattern ParamNotes- , starParamNotes- , ArmCoord (..)- , ArmCoords- , ParamEpError (..)- , mkParamNotes-- , EpLiftSequence (..)- , EntryPointCallT (..)- , epcPrimitive- , epcCallRootUnsafe- , SomeEntryPointCallT (..)- , sepcCallRootUnsafe- , sepcPrimitive- , sepcName- , ForbidOr- , MkEntryPointCallRes (..)- , mkEntryPointCall-- , tyImplicitAccountParam- , flattenEntryPoints-- -- * Re-exports- , EpName (..)- , pattern DefEpName- , epNameFromParamAnn- , epNameToParamAnn- , epNameFromRefAnn- , epNameToRefAnn- , EpNameFromRefAnnError (..)- ) where--import Control.Monad.Except (throwError)-import Data.Constraint (Dict(..))-import Data.Default (Default(..))-import qualified Data.List.NonEmpty as NE-import qualified Data.Text as T-import Fmt (Buildable(..), pretty, (+|), (|+))-import Hedgehog (GenBase, MonadGen)-import Test.QuickCheck (Arbitrary(..))--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, genAddress, parseAddress)-import Util.Test.Gen (genEpName)-import Util.TH-import Util.Typeable-import Util.TypeLits--------------------------------------------------------------------------------- Primitives----------------------------------------------------------------------------------- EpAddress--------------------------------------------------------------------------------- | Address with optional entrypoint name attached to it.--- TODO: come up with better name?-data EpAddress = EpAddress- { eaAddress :: Address- -- ^ Address itself- , eaEntryPoint :: EpName- -- ^ Entrypoint name (might be empty)- } 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- | otherwise = formatAddress addr <> "%" <> pretty ep--mformatEpAddress :: EpAddress -> MText-mformatEpAddress ea =- let t = formatEpAddress ea- -- Should be safe because set of characters allowed in annotations- -- (and thus in 'EpName') is subset of characters allowed in Michelson strings.- in mkMTextUnsafe t--data ParseEpAddressError- = ParseEpAddressBadAddress ParseAddressError- | ParseEpAddressBadRefAnn Text- | ParseEpAddressRefAnnError EpNameFromRefAnnError- deriving stock (Show, Eq, Generic)--instance NFData ParseEpAddressError--instance Buildable ParseEpAddressError where- build = \case- ParseEpAddressBadAddress err -> build err- ParseEpAddressBadRefAnn txt -> pretty $ "Invalid reference annotation: " <> txt- ParseEpAddressRefAnnError err -> build err---- | Parse an address which can be suffixed with entrypoint name--- (e.g. "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU%entrypoint").-parseEpAddress :: Text -> Either ParseEpAddressError EpAddress-parseEpAddress txt =- let (addrTxt, mannotTxt) = T.breakOn "%" txt- in case mannotTxt of- "" -> do- addr <- first ParseEpAddressBadAddress $ parseAddress addrTxt- return $ EpAddress addr def- annotTxt' -> do- addr <- first ParseEpAddressBadAddress $ parseAddress addrTxt- annot <- first ParseEpAddressBadRefAnn $ case T.stripPrefix "%" annotTxt' of- Nothing -> error "impossible"- Just a -> mkAnnotation a- epName <- first ParseEpAddressRefAnnError $ epNameFromRefAnn annot- return $ EpAddress addr epName--unsafeParseEpAddress :: HasCallStack => Text -> EpAddress-unsafeParseEpAddress = either (error . pretty) id . parseEpAddress--instance Arbitrary FieldAnn => Arbitrary EpAddress where- arbitrary = EpAddress <$> arbitrary <*> arbitrary--genEpAddress :: (MonadGen m, GenBase m ~ Identity) => m EpAddress-genEpAddress = EpAddress <$> genAddress <*> genEpName---- ParamNotes--------------------------------------------------------------------------------- | Annotations for contract parameter declaration.------ Following the Michelson specification, this type has the following invariants:--- 1. No entrypoint name is duplicated.--- 2. If @default@ entrypoint is explicitly assigned, no "arm" remains uncallable.-data ParamNotes (t :: T) = ParamNotesUnsafe- { pnNotes :: Notes t- , pnRootAnn :: RootAnn- } deriving stock (Show, Eq, Generic)- deriving anyclass (NFData)--pattern ParamNotes :: Notes t -> RootAnn -> ParamNotes t-pattern ParamNotes t f <- ParamNotesUnsafe t f-{-# COMPLETE ParamNotes #-}---- | Parameter without annotations.-starParamNotes :: SingI t => ParamNotes t-starParamNotes = ParamNotesUnsafe starNotes noAnn---- | Coordinates of "arm" in Or tree, used solely in error messages.-type ArmCoords = [ArmCoord]-data ArmCoord = AcLeft | AcRight- deriving stock (Show, Eq, Generic)--instance NFData ArmCoord--instance Buildable ArmCoord where- build = \case- AcLeft -> "left"- AcRight -> "right"---- | Errors specific to parameter type declaration (entrypoints).-data ParamEpError- = ParamEpDuplicatedNames (NonEmpty EpName)- | ParamEpUncallableArm ArmCoords- deriving stock (Show, Eq, Generic)--instance NFData ParamEpError--instance Buildable ParamEpError where- build = \case- ParamEpDuplicatedNames names -> mconcat- [ "Duplicated entrypoint names: "- , mconcat . intersperse ", " $ map (surround "'" "'" . build) (toList names)- ]- ParamEpUncallableArm arm -> mconcat- [ "Due to presence of 'default' entrypoint, one of contract \"arms\" \- \cannot be called: \""- , mconcat . intersperse " - " $ map build arm- , "\""- , if length arm > 1 then " (in top-to-bottom order)" else ""- ]- where- surround pre post builder = pre <> builder <> post---- | Check whether given notes are valid parameter notes.-verifyParamNotes :: Notes t -> RootAnn -> Either ParamEpError ()-verifyParamNotes notes ra = do- let allEps = appEndo (gatherEntrypoints notes) []- duplicatedEps- = mapMaybe (safeHead . tail)- . NE.group- . sort- $ maybe allEps (: allEps) (epNameFromParamAnn $ convAnn ra)-- whenJust (nonEmpty duplicatedEps) $ \dups ->- throwError $ ParamEpDuplicatedNames dups-- -- In case contract have explicit root entrypoint, we assume that everything is- -- callable.- when (ra == noAnn) $ void- $ first ParamEpUncallableArm- $ ensureAllCallable notes- where- gatherEntrypoints :: Notes t -> Endo [EpName]- gatherEntrypoints = \case- NTOr _ fn1 fn2 l r -> mconcat- [ Endo $ maybe id (:) (epNameFromParamAnn fn1)- , Endo $ maybe id (:) (epNameFromParamAnn fn2)- , gatherEntrypoints l- , gatherEntrypoints r- ]- _ -> mempty-- -- Here we can assume that there is no more than one @default@ entrypoint,- -- because duplications check occurs earlier.- --- -- In case when multiple entrypoints are uncallable, the reference- -- implementation prefers displaying (in error message) arms which are- -- closer to the root, but here we don't do this because that would be- -- some more complex to implement and not sure how much does it worth that.- ensureAllCallable :: Notes t -> Either ArmCoords Bool- ensureAllCallable = \case- NTOr _ fnL fnR l r -> do- let epNameL = epNameFromParamAnn fnL- let epNameR = epNameFromParamAnn fnR- haveDefLL <- first (AcLeft :) $ ensureAllCallable l- haveDefRR <- first (AcRight :) $ ensureAllCallable r-- let haveDefL = or [haveDefLL, epNameL == Just (def @EpName)]- let haveDefR = or [haveDefRR, epNameR == Just (def @EpName)]-- when haveDefL $ first (AcRight :) $ checkAllEpsNamed epNameR r- when haveDefR $ first (AcLeft :) $ checkAllEpsNamed epNameL l-- return $ or [haveDefL, haveDefR]-- _ -> return False-- checkAllEpsNamed :: Maybe EpName -> Notes t -> Either ArmCoords ()- checkAllEpsNamed epNameRoot- | isJust epNameRoot = \_ -> pass- | otherwise = \case- NTOr _ fnL fnR l r -> do- let epNameL = epNameFromParamAnn fnL- epNameR = epNameFromParamAnn fnR-- first (AcLeft :) $ checkAllEpsNamed epNameL l- first (AcRight :) $ checkAllEpsNamed epNameR r-- _ -> throwError []---- | Construct 'ParamNotes' performing all necessary checks.-mkParamNotes :: Notes t -> RootAnn -> Either ParamEpError (ParamNotes t)-mkParamNotes nt fa = verifyParamNotes nt fa $> ParamNotesUnsafe nt fa--------------------------------------------------------------------------------- Entrypoint logic--------------------------------------------------------------------------------- | Describes how to construct full contract parameter from given entrypoint--- argument.------ This could be just wrapper over @Value arg -> Value param@, but we cannot--- use @Value@ type in this module easily.-data EpLiftSequence (arg :: T) (param :: T) where- EplArgHere :: EpLiftSequence arg arg- EplWrapLeft- :: (KnownT subparam, KnownT r)- => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr subparam r)- EplWrapRight- :: (KnownT l, KnownT subparam)- => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr l subparam)--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 -> "×"- EplWrapLeft es -> "Left (" <> build es <> ")"- EplWrapRight es -> "Right (" <> build es <> ")"---- | Reference for calling a specific entrypoint of type @arg@.-data EntryPointCallT (param :: T) (arg :: T) =- ParameterScope arg => EntryPointCall- { epcName :: EpName- -- ^ Name of entrypoint.- , epcParamProxy :: Proxy param- -- ^ Proxy of parameter, to make parameter type more easily fetchable.- , epcLiftSequence :: EpLiftSequence arg param- -- ^ How to call this entrypoint in the corresponding contract.- }--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{..} =- "Call " +| epcName |+ ": " +| epcLiftSequence |+ ""---- | Construct 'EntryPointCallT' which calls no entrypoint and assumes that--- there is no explicit "default" one.------ Validity of such operation is not ensured.-epcCallRootUnsafe :: ParameterScope param => EntryPointCallT param param-epcCallRootUnsafe = EntryPointCall- { epcName = def- , epcParamProxy = Proxy- , epcLiftSequence = EplArgHere- }---- | Call parameter which has no entrypoints, always safe.-epcPrimitive- :: forall p.- (ParameterScope p, ForbidOr p)- => EntryPointCallT p p-epcPrimitive = epcCallRootUnsafe- where- _requireNoOr = Dict @(ForbidOr p)--type family ForbidOr (t :: T) :: Constraint where- ForbidOr ('TOr l r) =- TypeError- ('Text "Cannot apply to sum type parameter " ':<>: 'ShowType ('TOr l r))- ForbidOr _ = ()---- | 'EntryPointCallT' with hidden parameter type.------ This requires argument to satisfy 'ParameterScope' constraint.--- Strictly speaking, entrypoint argument may one day start having different--- set of constraints comparing to ones applied to parameter, but this seems--- unlikely.-data SomeEntryPointCallT (arg :: T) =- forall param. (ParameterScope param) =>- SomeEpc (EntryPointCallT param arg)--instance Eq (SomeEntryPointCallT arg) where- SomeEpc epc1 == SomeEpc epc2 = isJust @() $ do- Refl <- eqP (epcParamProxy epc1) (epcParamProxy epc2)- 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---- | Construct 'SomeEntryPointCallT' which calls no entrypoint and assumes that--- there is no explicit "default" one.------ Validity of such operation is not ensured.-sepcCallRootUnsafe :: ParameterScope param => SomeEntryPointCallT param-sepcCallRootUnsafe = SomeEpc epcCallRootUnsafe---- | Call parameter which has no entrypoints, always safe.-sepcPrimitive- :: forall t.- (ParameterScope t, ForbidOr t)- => SomeEntryPointCallT t-sepcPrimitive = SomeEpc epcPrimitive--sepcName :: SomeEntryPointCallT arg -> EpName-sepcName (SomeEpc epc) = epcName epc---- | Build 'EpLiftSequence'.------ Here we accept entrypoint name and type information for the parameter--- of target contract.------ Returns 'Nothing' if entrypoint is not found.--- Does not treat default entrypoints specially.-withEpLiftSequence- :: forall param r.- (ParameterScope param)- => EpName- -> Notes param- -> (forall arg. (ParameterScope arg) => (Notes arg, EpLiftSequence arg param) -> r)- -> Maybe r-withEpLiftSequence epName@(epNameToParamAnn -> epAnn) param cont =- 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 lNotes (cont . fmap @((,) _) EplWrapLeft)- , withEpLiftSequence epName rNotes (cont . fmap @((,) _) EplWrapRight)- ]- _ -> Nothing---- Helper datatype for 'mkEntryPointCall'.-data MkEntryPointCallRes param where- MkEntryPointCallRes- :: ParameterScope arg- => Notes arg- -> EntryPointCallT param arg- -> MkEntryPointCallRes param---- | Build 'EntryPointCallT'.------ Here we accept entrypoint name and type information for the parameter--- of target contract.------ Returns 'Nothing' if entrypoint is not found.-mkEntryPointCall- :: (ParameterScope param)- => EpName- -> ParamNotes param- -> Maybe (MkEntryPointCallRes param)-mkEntryPointCall epName (ParamNotes paramNotes root) =- asum- [ do- epName' <- epNameFromParamAnn $ convAnn root- guard (epName == epName')- return $ MkEntryPointCallRes- paramNotes- EntryPointCall- { epcName = epName- , epcParamProxy = Proxy- , epcLiftSequence = EplArgHere- }- , withEpLiftSequence epName paramNotes $ \(argInfo, liftSeq) ->- MkEntryPointCallRes argInfo $ EntryPointCall- { epcName = epName- , epcParamProxy = Proxy- , epcLiftSequence = liftSeq- }- , guard (epName == def) $>- MkEntryPointCallRes paramNotes EntryPointCall- { epcName = def- , epcParamProxy = Proxy- , epcLiftSequence = EplArgHere- }- ]---- | "Parameter" type of implicit account.-tyImplicitAccountParam :: ParamNotes 'TUnit-tyImplicitAccountParam = ParamNotesUnsafe starNotes noAnn---- 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 (pnNotes -> notes) = mkEntrypointsMap (mkUType notes)---- TODO [#35]: Root annotation is currently support as a dedicated field--- in 'ParamNotes'.------ Also it would be nice to automatically add @%root@ annotation in each parameter--- declaration when compiling Lorentz to Michelson.--- In addition to this, we should actually use @%root@ annotation in the entrypoint engine--- and not just store it.
+ src/Michelson/Typed/Entrypoints.hs view
@@ -0,0 +1,512 @@+-- SPDX-FileCopyrightText: 2020 Tocqueville Group+--+-- SPDX-License-Identifier: LicenseRef-MIT-TQ++-- | Utilities for lightweight entrypoints support.+module Michelson.Typed.Entrypoints+ ( EpAddress (..)+ , ParseEpAddressError (..)+ , formatEpAddress+ , mformatEpAddress+ , parseEpAddress+ , unsafeParseEpAddress+ , parseEpAddressRaw+ , unsafeParseEpAddressRaw+ , ParamNotes (..)+ , pattern ParamNotes+ , starParamNotes+ , ArmCoord (..)+ , ArmCoords+ , ParamEpError (..)+ , mkParamNotes++ , EpLiftSequence (..)+ , EntrypointCallT (..)+ , epcPrimitive+ , epcCallRootUnsafe+ , SomeEntrypointCallT (..)+ , sepcCallRootUnsafe+ , sepcPrimitive+ , sepcName+ , ForbidOr+ , MkEntrypointCallRes (..)+ , mkEntrypointCall++ , tyImplicitAccountParam+ , flattenEntrypoints++ -- * Re-exports+ , EpName (..)+ , pattern DefEpName+ , epNameFromParamAnn+ , epNameToParamAnn+ , epNameFromRefAnn+ , epNameToRefAnn+ , EpNameFromRefAnnError (..)+ ) where++import Control.Monad.Except (throwError)+import qualified Data.ByteString as BS+import Data.Constraint (Dict(..))+import Data.Default (Default(..))+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import Fmt (Buildable(..), hexF, pretty, (+|), (|+))+import Test.QuickCheck (Arbitrary(..))++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+import Tezos.Crypto (keyHashLengthBytes)+import Util.TH+import Util.Typeable+import Util.TypeLits++----------------------------------------------------------------------------+-- Primitives+----------------------------------------------------------------------------+--+-- EpAddress+----------------------------------------------------------------------------++-- | Address with optional entrypoint name attached to it.+-- TODO: come up with better name?+data EpAddress = EpAddress+ { eaAddress :: Address+ -- ^ Address itself+ , eaEntrypoint :: EpName+ -- ^ Entrypoint name (might be empty)+ } 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+ | otherwise = formatAddress addr <> "%" <> pretty ep++mformatEpAddress :: EpAddress -> MText+mformatEpAddress ea =+ let t = formatEpAddress ea+ -- Should be safe because set of characters allowed in annotations+ -- (and thus in 'EpName') is subset of characters allowed in Michelson strings.+ in mkMTextUnsafe t++data ParseEpAddressError+ = ParseEpAddressBadAddress ParseAddressError+ | ParseEpAddressRawBadAddress ParseAddressRawError+ | ParseEpAddressBadEntryopint ByteString UnicodeException+ | ParseEpAddressBadRefAnn Text+ | ParseEpAddressRefAnnError EpNameFromRefAnnError+ | ParseEpAddressInvalidLength Int+ deriving stock (Show, Eq, Generic)++instance NFData ParseEpAddressError++instance Buildable ParseEpAddressError where+ build = \case+ ParseEpAddressBadAddress err -> build err+ ParseEpAddressRawBadAddress err -> build err+ ParseEpAddressBadEntryopint addr exception ->+ "Invalid entrypoint given for raw adddress " <> hexF addr <>+ " and failed with " <> build (show @Text exception)+ ParseEpAddressBadRefAnn txt -> pretty $ "Invalid reference annotation: " <> txt+ ParseEpAddressRefAnnError err -> build err+ ParseEpAddressInvalidLength len ->+ "Given raw entrypoint address has invalid length: " <> build len++-- | Parse an address which can be suffixed with entrypoint name+-- (e.g. "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU%entrypoint").+parseEpAddress :: Text -> Either ParseEpAddressError EpAddress+parseEpAddress txt =+ let (addrTxt, mannotTxt) = T.breakOn "%" txt+ in case mannotTxt of+ "" -> do+ addr <- first ParseEpAddressBadAddress $ parseAddress addrTxt+ return $ EpAddress addr def+ annotTxt' -> do+ addr <- first ParseEpAddressBadAddress $ parseAddress addrTxt+ annot <- first ParseEpAddressBadRefAnn $ case T.stripPrefix "%" annotTxt' of+ Nothing -> error "impossible"+ Just a -> mkAnnotation a+ epName <- first ParseEpAddressRefAnnError $ epNameFromRefAnn annot+ return $ EpAddress addr epName++unsafeParseEpAddress :: HasCallStack => Text -> EpAddress+unsafeParseEpAddress = either (error . pretty) id . parseEpAddress++instance Arbitrary FieldAnn => Arbitrary EpAddress where+ arbitrary = EpAddress <$> arbitrary <*> arbitrary++-- | Parses byte representation of entrypoint address.+--+-- For every address+--+-- @+-- KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh%foo%bar+-- @+--+-- we get the following byte representation+--+-- @+-- 01afab866e7f1e74f9bba388d66b246276ce50bf4700666f6f25626172+-- \________________________________________/\/\____/\/\____/+-- address % ep1 % ep2+-- @+--+parseEpAddressRaw :: ByteString -> Either ParseEpAddressError EpAddress+parseEpAddressRaw raw = do+ let (bytes, eps) = BS.splitAt (keyHashLengthBytes + 2) raw+ eaAddress <- first ParseEpAddressRawBadAddress $ parseAddressRaw bytes+ decodedEntrypoint <- first (ParseEpAddressBadEntryopint raw) $ decodeUtf8' eps+ decodedAnnotation <- first ParseEpAddressBadRefAnn $ mkAnnotation decodedEntrypoint+ eaEntrypoint <- first ParseEpAddressRefAnnError $ epNameFromRefAnn decodedAnnotation+ pure $ EpAddress {..}++unsafeParseEpAddressRaw :: ByteString -> EpAddress+unsafeParseEpAddressRaw = either (error . pretty) id . parseEpAddressRaw++-- ParamNotes+----------------------------------------------------------------------------++-- | Annotations for contract parameter declaration.+--+-- Following the Michelson specification, this type has the following invariants:+-- 1. No entrypoint name is duplicated.+-- 2. If @default@ entrypoint is explicitly assigned, no "arm" remains uncallable.+data ParamNotes (t :: T) = ParamNotesUnsafe+ { pnNotes :: Notes t+ , pnRootAnn :: RootAnn+ } deriving stock (Show, Eq, Generic)+ deriving anyclass (NFData)++pattern ParamNotes :: Notes t -> RootAnn -> ParamNotes t+pattern ParamNotes t f <- ParamNotesUnsafe t f+{-# COMPLETE ParamNotes #-}++-- | Parameter without annotations.+starParamNotes :: SingI t => ParamNotes t+starParamNotes = ParamNotesUnsafe starNotes noAnn++-- | Coordinates of "arm" in Or tree, used solely in error messages.+type ArmCoords = [ArmCoord]+data ArmCoord = AcLeft | AcRight+ deriving stock (Show, Eq, Generic)++instance NFData ArmCoord++instance Buildable ArmCoord where+ build = \case+ AcLeft -> "left"+ AcRight -> "right"++-- | Errors specific to parameter type declaration (entrypoints).+data ParamEpError+ = ParamEpDuplicatedNames (NonEmpty EpName)+ | ParamEpUncallableArm ArmCoords+ deriving stock (Show, Eq, Generic)++instance NFData ParamEpError++instance Buildable ParamEpError where+ build = \case+ ParamEpDuplicatedNames names -> mconcat+ [ "Duplicated entrypoint names: "+ , mconcat . intersperse ", " $ map (surround "'" "'" . build) (toList names)+ ]+ ParamEpUncallableArm arm -> mconcat+ [ "Due to presence of 'default' entrypoint, one of contract \"arms\" \+ \cannot be called: \""+ , mconcat . intersperse " - " $ map build arm+ , "\""+ , if length arm > 1 then " (in top-to-bottom order)" else ""+ ]+ where+ surround pre post builder = pre <> builder <> post++-- | Check whether given notes are valid parameter notes.+verifyParamNotes :: Notes t -> RootAnn -> Either ParamEpError ()+verifyParamNotes notes ra = do+ let allEps = appEndo (gatherEntrypoints notes) []+ duplicatedEps+ = mapMaybe (safeHead . tail)+ . NE.group+ . sort+ $ maybe allEps (: allEps) (epNameFromParamAnn $ convAnn ra)++ whenJust (nonEmpty duplicatedEps) $ \dups ->+ throwError $ ParamEpDuplicatedNames dups++ -- In case contract have explicit root entrypoint, we assume that everything is+ -- callable.+ when (ra == noAnn) $ void+ $ first ParamEpUncallableArm+ $ ensureAllCallable notes+ where+ gatherEntrypoints :: Notes t -> Endo [EpName]+ gatherEntrypoints = \case+ NTOr _ fn1 fn2 l r -> mconcat+ [ Endo $ maybe id (:) (epNameFromParamAnn fn1)+ , Endo $ maybe id (:) (epNameFromParamAnn fn2)+ , gatherEntrypoints l+ , gatherEntrypoints r+ ]+ _ -> mempty++ -- Here we can assume that there is no more than one @default@ entrypoint,+ -- because duplications check occurs earlier.+ --+ -- In case when multiple entrypoints are uncallable, the reference+ -- implementation prefers displaying (in error message) arms which are+ -- closer to the root, but here we don't do this because that would be+ -- some more complex to implement and not sure how much does it worth that.+ ensureAllCallable :: Notes t -> Either ArmCoords Bool+ ensureAllCallable = \case+ NTOr _ fnL fnR l r -> do+ let epNameL = epNameFromParamAnn fnL+ let epNameR = epNameFromParamAnn fnR+ haveDefLL <- first (AcLeft :) $ ensureAllCallable l+ haveDefRR <- first (AcRight :) $ ensureAllCallable r++ let haveDefL = or [haveDefLL, epNameL == Just (def @EpName)]+ let haveDefR = or [haveDefRR, epNameR == Just (def @EpName)]++ when haveDefL $ first (AcRight :) $ checkAllEpsNamed epNameR r+ when haveDefR $ first (AcLeft :) $ checkAllEpsNamed epNameL l++ return $ or [haveDefL, haveDefR]++ _ -> return False++ checkAllEpsNamed :: Maybe EpName -> Notes t -> Either ArmCoords ()+ checkAllEpsNamed epNameRoot+ | isJust epNameRoot = \_ -> pass+ | otherwise = \case+ NTOr _ fnL fnR l r -> do+ let epNameL = epNameFromParamAnn fnL+ epNameR = epNameFromParamAnn fnR++ first (AcLeft :) $ checkAllEpsNamed epNameL l+ first (AcRight :) $ checkAllEpsNamed epNameR r++ _ -> throwError []++-- | Construct 'ParamNotes' performing all necessary checks.+mkParamNotes :: Notes t -> RootAnn -> Either ParamEpError (ParamNotes t)+mkParamNotes nt fa = verifyParamNotes nt fa $> ParamNotesUnsafe nt fa++----------------------------------------------------------------------------+-- Entrypoint logic+----------------------------------------------------------------------------++-- | Describes how to construct full contract parameter from given entrypoint+-- argument.+--+-- This could be just wrapper over @Value arg -> Value param@, but we cannot+-- use @Value@ type in this module easily.+data EpLiftSequence (arg :: T) (param :: T) where+ EplArgHere :: EpLiftSequence arg arg+ EplWrapLeft+ :: (KnownT subparam, KnownT r)+ => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr subparam r)+ EplWrapRight+ :: (KnownT l, KnownT subparam)+ => EpLiftSequence arg subparam -> EpLiftSequence arg ('TOr l subparam)++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 -> "×"+ EplWrapLeft es -> "Left (" <> build es <> ")"+ EplWrapRight es -> "Right (" <> build es <> ")"++-- | Reference for calling a specific entrypoint of type @arg@.+data EntrypointCallT (param :: T) (arg :: T) =+ ParameterScope arg => EntrypointCall+ { epcName :: EpName+ -- ^ Name of entrypoint.+ , epcParamProxy :: Proxy param+ -- ^ Proxy of parameter, to make parameter type more easily fetchable.+ , epcLiftSequence :: EpLiftSequence arg param+ -- ^ How to call this entrypoint in the corresponding contract.+ }++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{..} =+ "Call " +| epcName |+ ": " +| epcLiftSequence |+ ""++-- | Construct 'EntrypointCallT' which calls no entrypoint and assumes that+-- there is no explicit "default" one.+--+-- Validity of such operation is not ensured.+epcCallRootUnsafe :: ParameterScope param => EntrypointCallT param param+epcCallRootUnsafe = EntrypointCall+ { epcName = def+ , epcParamProxy = Proxy+ , epcLiftSequence = EplArgHere+ }++-- | Call parameter which has no entrypoints, always safe.+epcPrimitive+ :: forall p.+ (ParameterScope p, ForbidOr p)+ => EntrypointCallT p p+epcPrimitive = epcCallRootUnsafe+ where+ _requireNoOr = Dict @(ForbidOr p)++type family ForbidOr (t :: T) :: Constraint where+ ForbidOr ('TOr l r) =+ TypeError+ ('Text "Cannot apply to sum type parameter " ':<>: 'ShowType ('TOr l r))+ ForbidOr _ = ()++-- | 'EntrypointCallT' with hidden parameter type.+--+-- This requires argument to satisfy 'ParameterScope' constraint.+-- Strictly speaking, entrypoint argument may one day start having different+-- set of constraints comparing to ones applied to parameter, but this seems+-- unlikely.+data SomeEntrypointCallT (arg :: T) =+ forall param. (ParameterScope param) =>+ SomeEpc (EntrypointCallT param arg)++instance Eq (SomeEntrypointCallT arg) where+ SomeEpc epc1 == SomeEpc epc2 = isJust @() $ do+ Refl <- eqP (epcParamProxy epc1) (epcParamProxy epc2)+ 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++-- | Construct 'SomeEntrypointCallT' which calls no entrypoint and assumes that+-- there is no explicit "default" one.+--+-- Validity of such operation is not ensured.+sepcCallRootUnsafe :: ParameterScope param => SomeEntrypointCallT param+sepcCallRootUnsafe = SomeEpc epcCallRootUnsafe++-- | Call parameter which has no entrypoints, always safe.+sepcPrimitive+ :: forall t.+ (ParameterScope t, ForbidOr t)+ => SomeEntrypointCallT t+sepcPrimitive = SomeEpc epcPrimitive++sepcName :: SomeEntrypointCallT arg -> EpName+sepcName (SomeEpc epc) = epcName epc++-- | Build 'EpLiftSequence'.+--+-- Here we accept entrypoint name and type information for the parameter+-- of target contract.+--+-- Returns 'Nothing' if entrypoint is not found.+-- Does not treat default entrypoints specially.+withEpLiftSequence+ :: forall param r.+ (ParameterScope param)+ => EpName+ -> Notes param+ -> (forall arg. (ParameterScope arg) => (Notes arg, EpLiftSequence arg param) -> r)+ -> Maybe r+withEpLiftSequence epName@(epNameToParamAnn -> epAnn) param cont =+ 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 lNotes (cont . fmap @((,) _) EplWrapLeft)+ , withEpLiftSequence epName rNotes (cont . fmap @((,) _) EplWrapRight)+ ]+ _ -> Nothing++-- Helper datatype for 'mkEntrypointCall'.+data MkEntrypointCallRes param where+ MkEntrypointCallRes+ :: ParameterScope arg+ => Notes arg+ -> EntrypointCallT param arg+ -> MkEntrypointCallRes param++-- | Build 'EntrypointCallT'.+--+-- Here we accept entrypoint name and type information for the parameter+-- of target contract.+--+-- Returns 'Nothing' if entrypoint is not found.+mkEntrypointCall+ :: (ParameterScope param)+ => EpName+ -> ParamNotes param+ -> Maybe (MkEntrypointCallRes param)+mkEntrypointCall epName (ParamNotes paramNotes root) =+ asum+ [ do+ epName' <- epNameFromParamAnn $ convAnn root+ guard (epName == epName')+ return $ MkEntrypointCallRes+ paramNotes+ EntrypointCall+ { epcName = epName+ , epcParamProxy = Proxy+ , epcLiftSequence = EplArgHere+ }+ , withEpLiftSequence epName paramNotes $ \(argInfo, liftSeq) ->+ MkEntrypointCallRes argInfo $ EntrypointCall+ { epcName = epName+ , epcParamProxy = Proxy+ , epcLiftSequence = liftSeq+ }+ , guard (epName == def) $>+ MkEntrypointCallRes paramNotes EntrypointCall+ { epcName = def+ , epcParamProxy = Proxy+ , epcLiftSequence = EplArgHere+ }+ ]++-- | "Parameter" type of implicit account.+tyImplicitAccountParam :: ParamNotes 'TUnit+tyImplicitAccountParam = ParamNotesUnsafe starNotes noAnn++-- 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 (pnNotes -> notes) = mkEntrypointsMap (mkUType notes)++-- TODO [#35]: Root annotation is currently support as a dedicated field+-- in 'ParamNotes'.+--+-- Also it would be nice to automatically add @%root@ annotation in each parameter+-- declaration when compiling Lorentz to Michelson.+-- In addition to this, we should actually use @%root@ annotation in the entrypoint engine+-- and not just store it.
src/Michelson/Typed/Haskell/Doc.hs view
@@ -30,6 +30,7 @@ , homomorphicTypeDocHaskellRep , concreteTypeDocHaskellRep , concreteTypeDocHaskellRepUnsafe+ , haskellAddNewtypeField , haskellRepNoFields , haskellRepStripFieldPrefix , homomorphicTypeDocMichelsonRep@@ -64,7 +65,7 @@ import Michelson.Doc import Michelson.Text import Michelson.Typed.Aliases-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Haskell.ValidateDescription import Michelson.Typed.Haskell.Value import Michelson.Typed.T@@ -106,9 +107,11 @@ buildADTRep buildField = \case ConstructorRep{..} :| [] -> renderProduct (WithinParens False) crFields ps -> (mappend (mdItalic "one of" <> " \n")) $- mconcat . map (("+ " <> ) . (<> "\n")) $- map (renderNamedProduct (WithinParens True)) (toList ps)+ foldMap+ (toListItem . renderNamedProduct (WithinParens True)) (toList ps) where+ toListItem item = "+ " <> item <> "\n"+ renderNamedProduct :: WithinParens -> ConstructorRep a -> Markdown renderNamedProduct wp ConstructorRep{..} = mdBold (build crName) <>@@ -314,8 +317,8 @@ DType a1 `compare` DType a2 = a1 `compareExt` a2 instance DocItem DType where- type DocItemPosition DType = 5000 type DocItemPlacement DType = 'DocItemInDefinitions+ docItemPos = 5000 docItemSectionName = Just "Types" @@ -372,7 +375,7 @@ deriving stock Generic instance DocItem DStorageType where- type DocItemPosition DStorageType = 835+ docItemPos = 835 docItemSectionName = Just "Storage" docItemToMarkdown lvl (DStorageType t) = docItemToMarkdown lvl t docItemDependencies (DStorageType t) = docItemDependencies t@@ -512,6 +515,14 @@ haskellRepNoFields mkRep = \p descr -> second (mapADTRepFields (const Nothing)) <$> mkRep p descr +-- | Add field name for @newtype@.+--+-- Since @newtype@ field is automatically erased. Use this function+-- to add the desired field name.+haskellAddNewtypeField :: Text -> TypeDocHaskellRep a -> TypeDocHaskellRep a+haskellAddNewtypeField fieldName mkRep =+ \p descr -> second (mapADTRepFields (const (Just fieldName))) <$> mkRep p descr+ -- | Cut fields prefixes which we use according to the style guide. -- -- E.g. @cmMyField@ field will be transformed to @myField@.@@ -572,8 +583,12 @@ class GTypeHasDoc (x :: Kind.Type -> Kind.Type) where gTypeDocHaskellRep :: FieldDescriptionsV -> ADTRep SomeTypeWithDoc -instance GTypeHasDoc x => GTypeHasDoc (G.D1 i x) where+instance GTypeHasDoc x => GTypeHasDoc (G.D1 ('G.MetaData _a _b _c 'False) x) where gTypeDocHaskellRep = gTypeDocHaskellRep @x++instance GTypeHasDoc x => GTypeHasDoc (G.D1 ('G.MetaData _a _b _c 'True) x) where+ gTypeDocHaskellRep descr =+ mapADTRepFields (const Nothing) $ gTypeDocHaskellRep @x descr instance (GTypeHasDoc x, GTypeHasDoc y) => GTypeHasDoc (x :+: y) where gTypeDocHaskellRep descr = gTypeDocHaskellRep @x descr <> gTypeDocHaskellRep @y descr
src/Michelson/Typed/Haskell/Instr/Sum.hs view
@@ -50,7 +50,7 @@ import Unsafe.Coerce (unsafeCoerce) import Michelson.Text (MText, mt)-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Haskell.Instr.Helpers import Michelson.Typed.Haskell.Value import Michelson.Typed.Instr
src/Michelson/Typed/Haskell/Value.hs view
@@ -22,8 +22,8 @@ , Dict(..) -- * Re-exporting to use in tests. -- * Missing Haskell analogies to Michelson values- , EntryPointCall- , SomeEntryPointCall+ , EntrypointCall+ , SomeEntrypointCall , ContractRef (..) , coerceContractRef , contractRefToAddr@@ -35,9 +35,6 @@ , IsoValuesStack (..) , totsKnownLemma , totsAppendLemma-- -- * Generators- , genBigMap ) where import Data.Constraint ((:-)(..), Dict(..))@@ -49,15 +46,12 @@ import Fmt (Buildable(..)) import GHC.Generics ((:*:)(..), (:+:)(..)) import qualified GHC.Generics as G-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range import Named (NamedF(..)) import Test.QuickCheck (Arbitrary(..)) import Michelson.Text import Michelson.Typed.Aliases-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Sing import Michelson.Typed.T import Michelson.Typed.Value@@ -230,9 +224,9 @@ -- Types used specifically for conversion ---------------------------------------------------------------------------- -type EntryPointCall param arg = EntryPointCallT (ToT param) (ToT arg)+type EntrypointCall param arg = EntrypointCallT (ToT param) (ToT arg) -type SomeEntryPointCall arg = SomeEntryPointCallT (ToT arg)+type SomeEntrypointCall arg = SomeEntrypointCallT (ToT arg) type WellTypedToT a = WellTyped (ToT a) type WellTypedIsoValue a = (WellTyped (ToT a), IsoValue a)@@ -246,7 +240,7 @@ -- type argument. data ContractRef (arg :: Kind.Type) = ContractRef { crAddress :: Address- , crEntryPoint :: SomeEntryPointCall arg+ , crEntrypoint :: SomeEntrypointCall arg } deriving stock (Eq, Show) instance (WellTypedToT arg) => Buildable (ContractRef arg) where@@ -254,7 +248,7 @@ instance (WellTypedToT arg) => IsoValue (ContractRef arg) where type ToT (ContractRef arg) = 'TContract (ToT arg)- toVal ContractRef{..} = VContract crAddress crEntryPoint+ toVal ContractRef{..} = VContract crAddress crEntrypoint fromVal (VContract addr epc) = ContractRef addr epc -- | Replace type argument of 'ContractAddr' with isomorphic one.@@ -262,7 +256,7 @@ coerceContractRef ContractRef{..} = ContractRef{..} contractRefToAddr :: ContractRef cp -> EpAddress-contractRefToAddr ContractRef{..} = EpAddress crAddress (sepcName crEntryPoint)+contractRefToAddr ContractRef{..} = EpAddress crAddress (sepcName crEntrypoint) newtype BigMap k v = BigMap { unBigMap :: Map k v } deriving stock (Eq, Show)@@ -282,12 +276,6 @@ ) => Arbitrary (BigMap k v) where arbitrary = BigMap <$> arbitrary -genBigMap- :: forall k v m.- (MonadGen m, Ord k, WellTypedToT k, WellTypedToT v, Comparable (ToT k))- => m k -> m v -> m (BigMap k v)-genBigMap genK genV = BigMap <$> Gen.map (Range.linear 0 100) (liftA2 (,) genK genV)- -- Generic magic ---------------------------------------------------------------------------- @@ -299,6 +287,9 @@ -- A drawback of such approach is that, in theory, in new GHC version -- generified representation may change; however, chances are small and -- I (martoon) believe that contract versions will change much faster anyway.+--+-- In case an unbalanced tree is needed, the Generic instance can be derived by+-- using the utilities in the 'Util.Generics' module. class KnownT (GValueType x) => GIsoValue (x :: Kind.Type -> Kind.Type) where type GValueType x :: T
src/Michelson/Typed/Instr.hs view
@@ -17,6 +17,7 @@ , ContractCode , Contract (..) , mapContractCode+ , mapEntriesOrdered , pattern CAR , pattern CDR , pattern UNPAIR@@ -38,13 +39,13 @@ import Michelson.Printer.Util (RenderDoc(..), buildRenderDoc, needsParens, printDocS) import Michelson.Typed.Annotation (Notes(..)) import Michelson.Typed.Arith-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Polymorphic import Michelson.Typed.Scope import Michelson.Typed.Sing (KnownT) import Michelson.Typed.T (T(..)) import Michelson.Typed.Value (Comparable, ContractInp, ContractOut, Value'(..))-import Michelson.Untyped (Annotation(..), FieldAnn, VarAnn)+import Michelson.Untyped (Annotation(..), EntriesOrder(..), FieldAnn, VarAnn, entriesOrderToInt) import Util.Peano import Util.TH import Util.Type (type (++), KnownList)@@ -316,7 +317,7 @@ SELF :: forall (arg :: T) s . (ParameterScope arg)- => SomeEntryPointCallT arg+ => SomeEntrypointCallT arg -> Instr s ('TContract arg ': s) CONTRACT :: (ParameterScope p)@@ -453,17 +454,36 @@ { cCode :: ContractCode cp st , cParamNotes :: ParamNotes cp , cStoreNotes :: Notes st+ , cEntriesOrder :: EntriesOrder } deriving stock instance Show (Contract cp st) deriving stock instance Eq (ContractCode cp st) => Eq (Contract cp st) instance NFData (Contract cp st) where- rnf (Contract a b c) = rnf (a, b, c)+ rnf (Contract a b c d) = rnf (a, b, c, d) mapContractCode :: (ContractCode cp st -> ContractCode cp st) -> Contract cp st -> Contract cp st mapContractCode f contract = contract { cCode = f $ cCode contract }++-- | Map each typed contract fields by the given function and sort the output+-- based on the 'EntriesOrder'.+mapEntriesOrdered+ :: Contract cp st+ -> (ParamNotes cp -> a)+ -> (Notes st -> a)+ -> (ContractCode cp st -> a)+ -> [a]+mapEntriesOrdered Contract{..} fParam fStorage fCode =+ fmap snd+ $ sortWith fst+ [ (paramPos, fParam cParamNotes)+ , (storagePos, fStorage cStoreNotes)+ , (codePos, fCode cCode)+ ]+ where+ (paramPos, storagePos, codePos) = entriesOrderToInt cEntriesOrder $(deriveGADTNFData ''Instr)
src/Michelson/Typed/Polymorphic.hs view
@@ -22,9 +22,11 @@ import qualified Data.Set as S import Michelson.Text+import Michelson.Typed.Annotation import Michelson.Typed.Sing (KnownT) import Michelson.Typed.T (T(..)) import Michelson.Typed.Value (Value'(..))+import Michelson.Untyped.Annotation (noAnn) import Tezos.Core (divModMutez, divModMutezInt) @@ -174,6 +176,15 @@ class EDivOp (n :: T) (m :: T) where type EDivOpRes n m :: T type EModOpRes n m :: T++ -- | Converge the notes of given operands.+ convergeEDiv+ :: Notes n+ -> Notes m+ -> Either AnnConvergeError+ (Notes ('TOption ('TPair (EDivOpRes n m)+ (EModOpRes n m))))+ evalEDivOp :: Value' instr n -> Value' instr m@@ -183,6 +194,9 @@ instance EDivOp 'TInt 'TInt where type EDivOpRes 'TInt 'TInt = 'TInt type EModOpRes 'TInt 'TInt = 'TNat+ convergeEDiv n1 n2 =+ (\a -> NTOption noAnn $ NTPair noAnn noAnn noAnn a $ NTNat noAnn)+ <$> converge n1 n2 evalEDivOp (VInt i) (VInt j) = if j == 0 then VOption $ Nothing@@ -191,6 +205,8 @@ instance EDivOp 'TInt 'TNat where type EDivOpRes 'TInt 'TNat = 'TInt type EModOpRes 'TInt 'TNat = 'TNat+ convergeEDiv n1 _ = Right $ NTOption noAnn $ NTPair noAnn noAnn noAnn n1+ $ NTNat noAnn evalEDivOp (VInt i) (VNat j) = if j == 0 then VOption $ Nothing@@ -199,6 +215,8 @@ instance EDivOp 'TNat 'TInt where type EDivOpRes 'TNat 'TInt = 'TInt type EModOpRes 'TNat 'TInt = 'TNat+ convergeEDiv n1 _ = Right $ NTOption noAnn $ NTPair noAnn noAnn noAnn+ (NTInt noAnn) n1 evalEDivOp (VNat i) (VInt j) = if j == 0 then VOption $ Nothing@@ -207,6 +225,8 @@ instance EDivOp 'TNat 'TNat where type EDivOpRes 'TNat 'TNat = 'TNat type EModOpRes 'TNat 'TNat = 'TNat+ convergeEDiv n1 n2 = (\a -> NTOption noAnn $ NTPair noAnn noAnn noAnn a a)+ <$> converge n1 n2 evalEDivOp (VNat i) (VNat j) = if j == 0 then VOption $ Nothing@@ -215,6 +235,8 @@ instance EDivOp 'TMutez 'TMutez where type EDivOpRes 'TMutez 'TMutez = 'TNat type EModOpRes 'TMutez 'TMutez = 'TMutez+ convergeEDiv n1 n2 = (\a -> NTOption noAnn $ NTPair noAnn noAnn noAnn (NTNat noAnn) a)+ <$> converge n1 n2 evalEDivOp (VMutez i) (VMutez j) = VOption $ i `divModMutez` j <&> \case@@ -224,6 +246,7 @@ instance EDivOp 'TMutez 'TNat where type EDivOpRes 'TMutez 'TNat = 'TMutez type EModOpRes 'TMutez 'TNat = 'TMutez+ convergeEDiv n1 _ = Right $ NTOption noAnn $ NTPair noAnn noAnn noAnn n1 n1 evalEDivOp (VMutez i) (VNat j) = VOption $ i `divModMutezInt` j <&> \case
src/Michelson/Typed/Util.hs view
@@ -7,6 +7,7 @@ module Michelson.Typed.Util ( DfsSettings (..) , CtorEffectsApp (..)+ , ceaBottomToTop , dfsInstr , dfsFoldInstr , dfsModifyInstr@@ -105,12 +106,32 @@ LOOP_LEFT i1 -> recursion1 LOOP_LEFT i1 DIP i1 -> recursion1 DIP i1 DIPN s i1 -> recursion1 (DIPN s) i1- PUSH v -> case v of- VLam i1 | dsGoToValues ->- -- Using 'analyzeInstrFailure' here (and in case below) is cheap- -- (O(n) in total) because we never make it run over the same code twice- recursion1 (PUSH . VLam . analyzeInstrFailure) (rfAnyInstr i1)- _ -> step i++ -- This case is more complex so we duplicate @recursion1@ a bit.+ -- We have to traverse the pushed value because a lambda can be+ -- somewhere inside of it (e. g. one item of a pair).+ PUSH v -> fromMaybe (step i) do+ guard dsGoToValues+ let+ valueStep :: forall t . Value t -> (Value t, Maybe x)+ valueStep = \case+ -- Using 'analyzeInstrFailure' here (and in case below) is cheap+ -- (O(n) in total) because we never make it run over the same code twice+ VLam lambda -> bimap (VLam . analyzeInstrFailure) Just $+ dfsInstr settings step (rfAnyInstr lambda)+ otherV -> (otherV, Nothing)+ -- Note that @dfsValue@ does not respect 'CtorEffectsApp',+ -- so if we encounter a value that contains more than one lambda+ -- this function may misbehave.+ -- That's very unlikely in practice.+ -- In #264 we will support this feature in @dfsValue@.+ let+ (innerV, innerXMaybe) = dfsValue valueStep v++ innerX <- innerXMaybe+ let (outerI, outerX) = step $ PUSH innerV+ pure $ ceaApplyEffects dsCtorEffectsApp innerX outerX outerI+ LAMBDA (VLam i1) | dsGoToValues -> recursion1 (LAMBDA . VLam . analyzeInstrFailure) (rfAnyInstr i1)
src/Michelson/Typed/Value.hs view
@@ -45,7 +45,7 @@ import qualified Data.Kind as Kind import Michelson.Text (MText)-import Michelson.Typed.EntryPoints+import Michelson.Typed.Entrypoints import Michelson.Typed.Sing import Michelson.Typed.Scope (BadTypeForScope(..), CheckScope(..), ParameterScope, StorageScope) import Michelson.Typed.T (T(..))@@ -340,7 +340,7 @@ :: Operation' instr -> Value' instr 'TOperation VContract :: forall arg instr.- Address -> SomeEntryPointCallT arg -> Value' instr ('TContract arg)+ Address -> SomeEntrypointCallT arg -> Value' instr ('TContract arg) VPair :: forall l r instr. (Value' instr l, Value' instr r) -> Value' instr ('TPair l r)@@ -423,7 +423,7 @@ -- | Lift entrypoint argument to full parameter. liftCallArg- :: EntryPointCallT param arg+ :: EntrypointCallT param arg -> Value' instr arg -> Value' instr param liftCallArg epc = compileEpLiftSequence (epcLiftSequence epc)@@ -447,7 +447,7 @@ VList{} -> Dict VSet{} -> Dict VOp{} -> Dict- VContract _ (SomeEpc EntryPointCall{}) -> Dict+ VContract _ (SomeEpc EntrypointCall{}) -> Dict VPair (l, r) -> case (valueTypeSanity l, valueTypeSanity r) of (Dict, Dict) -> Dict VOr{} -> Dict
src/Michelson/Untyped.hs view
@@ -9,7 +9,7 @@ import Michelson.Untyped.Aliases as Exports import Michelson.Untyped.Annotation as Exports import Michelson.Untyped.Contract as Exports-import Michelson.Untyped.EntryPoints as Exports+import Michelson.Untyped.Entrypoints as Exports import Michelson.Untyped.Ext as Exports import Michelson.Untyped.Instr as Exports import Michelson.Untyped.Type as Exports
src/Michelson/Untyped/Contract.hs view
@@ -5,36 +5,123 @@ -- | Michelson contract in untyped model. module Michelson.Untyped.Contract- ( Storage+ ( EntriesOrder (..)+ , canonicalEntriesOrder+ , entriesOrderToInt+ , mapEntriesOrdered++ , ContractBlock (..)+ , orderContractBlock+ , Contract' (..)+ , Storage ) where import Data.Aeson.TH (deriveJSON) import Data.Data (Data(..))-import Formatting.Buildable (Buildable(build))-import Text.PrettyPrint.Leijen.Text (nest, semi, (<$$>), (<+>))+import Data.Default (Default(..))+import Fmt (Buildable(build))+import Text.PrettyPrint.Leijen.Text (nest, semi, text, (<$$>), (<+>)) import Michelson.Printer.Util (Prettier(..), RenderDoc(..), assertParensNotNeeded, buildRenderDoc, needsParens, renderOpsList) import Michelson.Untyped.Type (ParameterType(..), Type(..)) import Util.Aeson +-- | Top-level entries order of the contract.+-- This is preserved due to the fact that it affects+-- the output of pretty-printing and serializing contract.++-- Each constructors is created by the order of the first letter of+-- @parameter@, @storage@, and @code@.+--+-- For example, @PSC@ would be @parameter@, @storage@ and @code@,+-- @CPS@ would be @code@, @parameter@,and @storage@, and so on.+data EntriesOrder+ = PSC+ | PCS+ | SPC+ | SCP+ | CSP+ | CPS+ deriving stock (Bounded, Data, Enum, Eq, Generic, Show)++instance Default EntriesOrder where+ def = canonicalEntriesOrder+instance NFData EntriesOrder++-- | The canonical entries order which is ordered as follow:+-- @parameter@, @storage@, and @code@.+canonicalEntriesOrder :: EntriesOrder+canonicalEntriesOrder = PSC++-- | @(Int, Int, Int)@ is the positions of @parameter@, @storage@, and @code@+-- respectively.+entriesOrderToInt :: EntriesOrder -> (Int, Int, Int)+entriesOrderToInt = \case+ PSC -> (0, 1, 2)+ PCS -> (0, 2, 1)+ SPC -> (1, 0, 2)+ SCP -> (1, 2, 0)+ CSP -> (2, 1, 0)+ CPS -> (2, 0, 1)++-- | Contract block, convenient when parsing+data ContractBlock op+ = CBParam ParameterType+ | CBStorage Type+ | CBCode [op]+ deriving stock (Eq, Show)++orderContractBlock :: (ContractBlock op, ContractBlock op, ContractBlock op) -> Maybe (Contract' op)+orderContractBlock = \case+ (CBParam p, CBStorage s, CBCode c) -> Just $ Contract p s c PSC+ (CBParam p, CBCode c, CBStorage s) -> Just $ Contract p s c PCS+ (CBStorage s, CBParam p, CBCode c) -> Just $ Contract p s c SPC+ (CBStorage s, CBCode c, CBParam p) -> Just $ Contract p s c SCP+ (CBCode c, CBStorage s, CBParam p) -> Just $ Contract p s c CSP+ (CBCode c, CBParam p, CBStorage s) -> Just $ Contract p s c CPS+ _ -> Nothing++-- | Map each contract fields by the given function and sort the output+-- based on the 'EntriesOrder'.+mapEntriesOrdered+ :: Contract' op+ -> (ParameterType -> a)+ -> (Storage -> a)+ -> ([op] -> a)+ -> [a]+mapEntriesOrdered Contract{..} fParam fStorage fCode =+ fmap snd+ $ sortWith fst+ [ (paramPos, fParam contractParameter)+ , (storagePos, fStorage contractStorage)+ , (codePos, fCode contractCode)+ ]+ where+ (paramPos, storagePos, codePos) = entriesOrderToInt entriesOrder+ type Storage = Type data Contract' op = Contract { contractParameter :: ParameterType , contractStorage :: Storage , contractCode :: [op]+ , entriesOrder :: EntriesOrder } 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 <$$>- "storage" <+> renderDoc needsParens (Prettier storage) <> semi <$$>- "code" <+> nest (length ("code {" :: Text)) (renderOpsList False code <> semi)+ renderDoc pn contract =+ assertParensNotNeeded pn+ $ foldr (<$$>) (text "")+ $ mapEntriesOrdered contract+ (\parameter -> "parameter" <+> renderDoc needsParens (Prettier parameter) <> semi)+ (\storage -> "storage" <+> renderDoc needsParens (Prettier storage) <> semi)+ (\code -> "code" <+> nest (length ("code {" :: Text)) (renderOpsList False code <> semi)) instance RenderDoc op => Buildable (Contract' op) where build = buildRenderDoc +deriveJSON morleyAesonOptions ''EntriesOrder deriveJSON morleyAesonOptions ''Contract'
− src/Michelson/Untyped/EntryPoints.hs
@@ -1,128 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Michelson.Untyped.EntryPoints- ( EpName (..)- , pattern DefEpName- , epNameFromParamAnn- , epNameToParamAnn- , epNameFromRefAnn- , epNameToRefAnn- , EpNameFromRefAnnError (..)- , buildEpName- , unsafeBuildEpName- , mkEntrypointsMap- ) where--import Data.Aeson.TH (deriveJSON)-import Data.Default (Default(..))-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.Aeson-import Util.CLI---- | Entrypoint name.------ Empty if this entrypoint is default one.--- 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 stock (Show, Eq, Ord, Generic)--instance NFData EpName--deriveJSON morleyAesonOptions ''EpName--pattern DefEpName :: EpName-pattern DefEpName = EpNameUnsafe ""--instance Buildable EpName where- build = \case- DefEpName -> "<default>"- EpNameUnsafe name -> build name--instance Default EpName where- def = EpNameUnsafe ""---- | Make up 'EpName' from annotation in parameter type declaration.------ Returns 'Nothing' if no entrypoint is assigned here.-epNameFromParamAnn :: FieldAnn -> Maybe EpName-epNameFromParamAnn an@(Annotation a)- | an == noAnn = Nothing- | a == "default" = Just (EpNameUnsafe "")- | otherwise = Just $ EpNameUnsafe a---- | Turn entrypoint name into annotation for contract parameter declaration.-epNameToParamAnn :: EpName -> FieldAnn-epNameToParamAnn (EpNameUnsafe name)- | name == "" = ann "default"- | otherwise = ann name--data EpNameFromRefAnnError- = InEpNameBadAnnotation FieldAnn- deriving stock (Show, Eq, Generic)--instance NFData EpNameFromRefAnnError--instance Buildable EpNameFromRefAnnError where- build = \case- InEpNameBadAnnotation (Annotation an) ->- "Invalid entrypoint reference `" +| an |+ "`"---- | Make up 'EpName' from annotation which is reference to an entrypoint--- (e.g. annotation in @CONTRACT@ instruction).------ Fails if annotation is invalid.-epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName-epNameFromRefAnn an@(Annotation a)- | a == "default" = Left $ InEpNameBadAnnotation an- | otherwise = Right $ EpNameUnsafe a---- | Turn entrypoint name into annotation used as reference to entrypoint.-epNameToRefAnn :: EpName -> FieldAnn-epNameToRefAnn (EpNameUnsafe name) = ann name--buildEpName :: Text -> Either String EpName-buildEpName txt = do- annotation <-- mkAnnotation txt- & first (mappend "Failed to parse entrypoint: " . pretty)- epNameFromRefAnn annotation- & first pretty--unsafeBuildEpName :: Text -> EpName-unsafeBuildEpName = either (error . pretty) id . buildEpName--instance Arbitrary FieldAnn => Arbitrary EpName where- arbitrary = arbitrary `suchThatMap` (rightToMaybe . epNameFromRefAnn)--instance HasCLReader EpName where- getReader = eitherReader (buildEpName . toText)- 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/Entrypoints.hs view
@@ -0,0 +1,128 @@+-- SPDX-FileCopyrightText: 2020 Tocqueville Group+--+-- SPDX-License-Identifier: LicenseRef-MIT-TQ++module Michelson.Untyped.Entrypoints+ ( EpName (..)+ , pattern DefEpName+ , epNameFromParamAnn+ , epNameToParamAnn+ , epNameFromRefAnn+ , epNameToRefAnn+ , EpNameFromRefAnnError (..)+ , buildEpName+ , unsafeBuildEpName+ , mkEntrypointsMap+ ) where++import Data.Aeson.TH (deriveJSON)+import Data.Default (Default(..))+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.Aeson+import Util.CLI++-- | Entrypoint name.+--+-- Empty if this entrypoint is default one.+-- 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 stock (Show, Eq, Ord, Generic)++instance NFData EpName++deriveJSON morleyAesonOptions ''EpName++pattern DefEpName :: EpName+pattern DefEpName = EpNameUnsafe ""++instance Buildable EpName where+ build = \case+ DefEpName -> "<default>"+ EpNameUnsafe name -> build name++instance Default EpName where+ def = EpNameUnsafe ""++-- | Make up 'EpName' from annotation in parameter type declaration.+--+-- Returns 'Nothing' if no entrypoint is assigned here.+epNameFromParamAnn :: FieldAnn -> Maybe EpName+epNameFromParamAnn an@(Annotation a)+ | an == noAnn = Nothing+ | a == "default" = Just (EpNameUnsafe "")+ | otherwise = Just $ EpNameUnsafe a++-- | Turn entrypoint name into annotation for contract parameter declaration.+epNameToParamAnn :: EpName -> FieldAnn+epNameToParamAnn (EpNameUnsafe name)+ | name == "" = ann "default"+ | otherwise = ann name++data EpNameFromRefAnnError+ = InEpNameBadAnnotation FieldAnn+ deriving stock (Show, Eq, Generic)++instance NFData EpNameFromRefAnnError++instance Buildable EpNameFromRefAnnError where+ build = \case+ InEpNameBadAnnotation (Annotation an) ->+ "Invalid entrypoint reference `" +| an |+ "`"++-- | Make up 'EpName' from annotation which is reference to an entrypoint+-- (e.g. annotation in @CONTRACT@ instruction).+--+-- Fails if annotation is invalid.+epNameFromRefAnn :: FieldAnn -> Either EpNameFromRefAnnError EpName+epNameFromRefAnn an@(Annotation a)+ | a == "default" = Left $ InEpNameBadAnnotation an+ | otherwise = Right $ EpNameUnsafe a++-- | Turn entrypoint name into annotation used as reference to entrypoint.+epNameToRefAnn :: EpName -> FieldAnn+epNameToRefAnn (EpNameUnsafe name) = ann name++buildEpName :: Text -> Either String EpName+buildEpName txt = do+ annotation <-+ mkAnnotation txt+ & first (mappend "Failed to parse entrypoint: " . pretty)+ epNameFromRefAnn annotation+ & first pretty++unsafeBuildEpName :: Text -> EpName+unsafeBuildEpName = either (error . pretty) id . buildEpName++instance Arbitrary FieldAnn => Arbitrary EpName where+ arbitrary = arbitrary `suchThatMap` (rightToMaybe . epNameFromRefAnn)++instance HasCLReader EpName where+ getReader = eitherReader (buildEpName . toText)+ 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/Type.hs view
@@ -37,7 +37,7 @@ import Data.Aeson.TH (deriveJSON) import Data.Data (Data(..))-import Formatting.Buildable (Buildable(build))+import Fmt (Buildable(build)) import Language.Haskell.TH.Lift (deriveLift) import Prelude hiding ((<$>)) import Text.PrettyPrint.Leijen.Text (Doc, align, softbreak, (<$>), (<+>))
src/Michelson/Untyped/Value.hs view
@@ -15,7 +15,7 @@ import Data.Aeson (FromJSON(..), ToJSON(..), withText) import Data.Aeson.TH (deriveJSON) import Data.Data (Data(..))-import Formatting.Buildable (Buildable(build))+import Fmt (Buildable(build)) import Text.Hex (decodeHex, encodeHex) import Text.PrettyPrint.Leijen.Text (Doc, braces, dquotes, enclose, semi, space, text, textStrict, (<+>))
src/Morley/Micheline/Binary.hs view
@@ -12,9 +12,9 @@ , encodeExpression ) where -import qualified Data.Binary.Get as Bi import qualified Data.Binary.Builder as Bi-import Data.Bits (Bits, (.&.), (.|.), bit, setBit, shift, testBit, zeroBits)+import qualified Data.Binary.Get as Bi+import Data.Bits (Bits, bit, setBit, shift, testBit, zeroBits, (.&.), (.|.)) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import Data.Sequence (Seq((:<|)))@@ -134,6 +134,7 @@ len <- fromIntegral <$> Bi.getWord32be DynamicSize <$> Bi.isolate len getter +{-# ANN getText ("HLint: ignore Redundant fmap" :: Text) #-} getText :: Bi.Get Text getText = fmap decodeUtf8' getByteString >>= \case
src/Morley/Micheline/Class.hs view
@@ -6,21 +6,28 @@ -- Micheline representation. module Morley.Micheline.Class ( ToExpression (..)+ , FromExpressionError (..) , FromExpression (..) ) where +import qualified Data.ByteString.Lazy as LBS import Data.Sequence (fromList, (|>)) import Data.Singletons (pattern FromSing, Sing, SingI, withSingI)+import Fmt (Buildable(..), pretty) import Michelson.Interpret.Pack (encodeValue', packCode', packNotedT', packT')-import Michelson.Interpret.Unpack (unpackInstr', unpackValue')+import Michelson.Interpret.Unpack (UnpackError, decodeContract, unpackInstr', unpackValue') import Michelson.Typed (Contract(..), HasNoOp, Instr(..), Notes(..), T(..), Value, pnNotes, pnRootAnn)+import Michelson.Typed.Instr (mapEntriesOrdered) import Michelson.Typed.Scope (UnpackedValScope)-import Michelson.Untyped.Annotation (RootAnn, convAnn)+import qualified Michelson.Untyped as Untyped+import Michelson.Untyped.Annotation (RootAnn, convAnn, noAnn) import Michelson.Untyped.Instr (ExpandedOp) import Morley.Micheline.Binary (decodeExpression, encodeExpression)-import Morley.Micheline.Expression (Annotation (..), Expression(..), MichelinePrimAp(..), MichelinePrimitive(..))+import Morley.Micheline.Expression+ (Annotation(..), Expression(..), MichelinePrimAp(..), MichelinePrimitive(..))+import Util.Binary (launchGet) -- | Type class that provides an ability to convert -- something to Micheline Expression.@@ -41,39 +48,64 @@ toExpression = decodeExpression . encodeValue' instance ToExpression (Contract cp st) where- toExpression Contract{..} = ExpressionSeq $ fromList- [ ExpressionPrim $- MichelinePrimAp (MichelinePrimitive "parameter")- (fromList [ addRootAnnToExpression (pnRootAnn cParamNotes) $- toExpression $ pnNotes cParamNotes- ])- (fromList [])- , ExpressionPrim $- MichelinePrimAp (MichelinePrimitive "storage")- (fromList [toExpression $ cStoreNotes])- (fromList [])- , ExpressionPrim $- MichelinePrimAp (MichelinePrimitive "code")- (fromList [toExpression cCode])- (fromList [])- ]+ toExpression contract@Contract{..} = ExpressionSeq $ fromList $ mapEntriesOrdered contract+ (\param -> ExpressionPrim $+ MichelinePrimAp (MichelinePrimitive "parameter")+ (fromList [ addRootAnnToExpression (pnRootAnn param) $+ toExpression $ pnNotes param+ ])+ (fromList [])+ )+ (\store -> ExpressionPrim $+ MichelinePrimAp (MichelinePrimitive "storage")+ (fromList [toExpression $ store])+ (fromList [])+ )+ (\code -> ExpressionPrim $+ MichelinePrimAp (MichelinePrimitive "code")+ (fromList [toExpression code])+ (fromList [])+ ) where- addRootAnnToExpression :: RootAnn -> Expression -> Expression- addRootAnnToExpression rootAnn = \case- ExpressionPrim p -> ExpressionPrim- p{ mpaAnnots = mpaAnnots p |>+ addRootAnnToExpression :: HasCallStack =>+ RootAnn -> Expression -> Expression+ addRootAnnToExpression rootAnn expr = case expr of+ ExpressionPrim p+ | rootAnn /= noAnn -> ExpressionPrim p+ { mpaAnnots = mpaAnnots p |> (AnnotationField $ convAnn rootAnn)- }- x -> x+ }+ | otherwise -> expr+ -- Currently this error can't happen because parameter type+ -- must be a Micheline primitive. If it ever changes, we+ -- would like to notice it ASAP and update this place.+ _ -> error $ "parameter is not a primitive: " <> show expr +-- | Errors that can happen when we convert an 'Expression' to our+-- data type.+data FromExpressionError = FromExpressionError UnpackError+ deriving stock (Show, Eq)++instance Buildable FromExpressionError where+ build (FromExpressionError err) = build err++instance Exception FromExpressionError where+ displayException = pretty+ -- | Type class that provides the ability to convert -- something from a Micheline Expression. class FromExpression a where- fromExpression :: Expression -> Maybe a+ fromExpression :: Expression -> Either FromExpressionError a instance UnpackedValScope t => FromExpression (Value t) where -- | `05` is the prefix for serialized Michelson value.- fromExpression = rightToMaybe . unpackValue' . ("\05" <>) . encodeExpression+ fromExpression =+ first FromExpressionError . unpackValue' . ("\05" <>) . encodeExpression instance FromExpression [ExpandedOp] where- fromExpression = rightToMaybe . unpackInstr' . encodeExpression+ fromExpression = first FromExpressionError . unpackInstr' . encodeExpression++instance FromExpression Untyped.Contract where+ fromExpression =+ first FromExpressionError . launchGet decodeContract .+ LBS.fromStrict . encodeExpression
src/Morley/Micheline/Expression.hs view
@@ -14,13 +14,10 @@ , michelsonPrimitive , annotToText , annotFromText- , genExpression- , genExpressionSeq- , genMichelinePrimAp- , genExprAnnotation ) where -import Data.Aeson (FromJSON, ToJSON, parseJSON, toEncoding, toJSON, withObject, withText, (.!=), (.:), (.:?))+import Data.Aeson+ (FromJSON, ToJSON, parseJSON, toEncoding, toJSON, withObject, withText, (.!=), (.:), (.:?)) import Data.Aeson.Casing (aesonPrefix, snakeCase) import qualified Data.Aeson.Encoding.Internal as Aeson import Data.Aeson.TH (deriveToJSON)@@ -28,17 +25,14 @@ import qualified Data.HashMap.Strict as HashMap import qualified Data.Sequence as Seq import qualified Data.Text as T (uncons)-import Fmt (pretty)-import Hedgehog (MonadGen(GenBase))-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range+import Fmt (Buildable(..), pretty, (+|), (|+)) -import qualified Michelson.Untyped.Annotation as MUA (Annotation) import Michelson.Untyped.Annotation- (KnownAnnTag(..), FieldAnn, TypeAnn, VarAnn, TypeTag, FieldTag, VarTag, ann, annPrefix)+ (FieldAnn, FieldTag, KnownAnnTag(..), TypeAnn, TypeTag, VarAnn, VarTag, ann, annPrefix)+import qualified Michelson.Untyped.Annotation as MUA (Annotation) import Morley.Micheline.Json+import Tezos.Crypto (encodeBase58Check) import Util.ByteString (HexJSONByteString(..))-import Util.Test.Gen (genAnnotation) newtype MichelinePrimitive = MichelinePrimitive Text deriving newtype (Eq, Ord, ToJSON, FromJSON)@@ -78,6 +72,22 @@ | ExpressionPrim MichelinePrimAp deriving stock (Eq, Show) +instance Buildable Expression where+ build = \case+ ExpressionInt i -> build $ i+ ExpressionString s -> build s+ ExpressionBytes b ->+ build $ encodeBase58Check b+ ExpressionSeq s -> "(" +| buildSeq build s |+ ")"+ ExpressionPrim (MichelinePrimAp (MichelinePrimitive text) s annots) ->+ text <> " " |+ "(" +|+ buildSeq build s +| ") " +|+ buildSeq (build . annotToText) annots+ where+ buildSeq buildElem =+ mconcat . intersperse ", " . map+ buildElem . toList+ data Annotation = AnnotationType TypeAnn | AnnotationVariable VarAnn@@ -96,8 +106,6 @@ <*> v .:? "args" .!= mempty <*> v .:? "annots" .!= mempty -deriveToJSON (aesonPrefix snakeCase) ''MichelinePrimAp- annotFromText :: MonadFail m => Text -> m Annotation annotFromText txt = case result of Just a -> pure a@@ -148,36 +156,4 @@ toEncoding (ExpressionInt x) = Aeson.pairs (Aeson.pair "int" (toEncoding $ StringEncode x)) toEncoding (ExpressionBytes x) = Aeson.pairs (Aeson.pair "bytes" (toEncoding $ HexJSONByteString x)) --------------------------------------------------------- Gen Function for testing--------------------------------------------------------genExpression :: forall m. (MonadGen m, GenBase m ~ Identity) => m Expression-genExpression = Gen.recursive Gen.choice- [genExpressionInt, genExpressionString, genExpressionBytes]- [genSeq, genExpressionPrim]- where- genExpressionInt = ExpressionInt <$> (Gen.integral (Range.linearFrom 0 -1000 1000))- genExpressionString = ExpressionString <$> (Gen.text (Range.linear 0 10) Gen.unicodeAll)- genExpressionBytes = ExpressionBytes <$> (Gen.bytes (Range.linear 0 100))- genSeq = ExpressionSeq <$> genExpressionSeq- genExpressionPrim = ExpressionPrim <$> genMichelinePrimAp--genExpressionSeq :: forall m. (MonadGen m, GenBase m ~ Identity) => m (Seq Expression)-genExpressionSeq = (Gen.seq (Range.linear 0 10) genExpression)--genMichelinePrimAp :: forall m. (MonadGen m, GenBase m ~ Identity) => m MichelinePrimAp-genMichelinePrimAp = MichelinePrimAp <$> genMichelinePrimitive <*> genExpressionSeq <*> genAnnots- where- genMichelinePrimitive = MichelinePrimitive <$> (Gen.element $ toList michelsonPrimitive)-- genAnnots :: m (Seq Annotation)- genAnnots = Gen.seq (Range.linear 0 10) genExprAnnotation--genExprAnnotation :: forall m. (MonadGen m, GenBase m ~ Identity) => m Annotation-genExprAnnotation = Gen.choice- [genAnnotationType, genAnnotationVariable, genAnnotationField]- where- genAnnotationType = AnnotationType <$> genAnnotation- genAnnotationVariable = AnnotationVariable <$> genAnnotation- genAnnotationField = AnnotationField <$> genAnnotation+deriveToJSON (aesonPrefix snakeCase) ''MichelinePrimAp
src/Tezos/Address.hs view
@@ -14,14 +14,17 @@ , OriginationIndex (..) , mkContractAddress , mkContractHashHack- , genAddress -- * Formatting , ParseAddressError (..)- , ParseContractAddressError+ , ParseAddressRawError (..)+ , ParseContractAddressError (..) , formatAddress , mformatAddress+ , parseAddressRaw+ , parseContractHash , parseAddress+ , unsafeParseAddressRaw , unsafeParseAddress , unsafeParseContractHash ) where@@ -33,11 +36,7 @@ import Data.Binary.Put (putInt32be, runPut) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL-import Fmt (fmt, hexF, pretty)-import qualified Formatting.Buildable as Buildable-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range+import Fmt (Buildable(build), hexF, pretty) import Test.QuickCheck (Arbitrary(..), oneof, vector) import Michelson.Text@@ -51,6 +50,13 @@ instance NFData ContractHash +-- We know that the length must be 20.+-- Currently it's hardcoded here, later we'll probably have a `Hash` type.+-- | Length of contract hash in bytes (only hash itself, no tags, checksums+-- or anything).+contractHashLengthBytes :: Integral n => n+contractHashLengthBytes = 20+ -- | Data type corresponding to address structure in Tezos. data Address = KeyAddress KeyHash@@ -120,6 +126,11 @@ -- Formatting/parsing ---------------------------------------------------------------------------- +-- It's a magic constant used by Tezos to encode a contract address.+-- It was deduced empirically.+contractAddressPrefix :: ByteString+contractAddressPrefix = "\2\90\121"+ formatContractHash :: ContractHash -> Text formatContractHash (ContractHash bs) = encodeBase58Check (contractAddressPrefix <> bs)@@ -133,8 +144,8 @@ mformatAddress :: Address -> MText mformatAddress = mkMTextUnsafe . formatAddress -instance Buildable.Buildable Address where- build = Buildable.build . formatAddress+instance Buildable Address where+ build = build . formatAddress -- | Errors that can happen during address parsing. data ParseAddressError@@ -146,16 +157,16 @@ instance NFData ParseAddressError -instance Buildable.Buildable ParseAddressError where+instance Buildable ParseAddressError where build = \case ParseAddressWrongBase58Check -> "Wrong base58check format" ParseAddressBothFailed pkErr contractErr -> mconcat [ "Address is neither `KeyAddress` ("- , Buildable.build pkErr+ , build pkErr , "), nor `ContractAddress` ("- , Buildable.build contractErr+ , build contractErr , ")" ] @@ -170,52 +181,105 @@ ContractAddress <$> parseContractHash addressText Right keyHash -> Right (KeyAddress keyHash) --- | Partial version of 'parseAddress' which assumes that the address--- is correct. Can be used in tests.-unsafeParseAddress :: HasCallStack => Text -> Address-unsafeParseAddress = either (error . pretty) id . parseAddress+data ParseAddressRawError+ = ParseAddressRawWrongSize ByteString+ -- ^ Raw bytes representation of an address has invalid length.+ | ParseAddressRawInvalidPrefix ByteString+ -- ^ Raw bytes representation of an address has incorrect prefix.+ | ParseAddressRawMalformedSeparator ByteString+ -- ^ Raw bytes representation of an address does not end with "\00".+ deriving stock (Eq, Show, Generic) +instance NFData ParseAddressRawError++instance Buildable ParseAddressRawError where+ build =+ \case+ ParseAddressRawInvalidPrefix prefix ->+ "Invalid prefix for raw address \"" <> hexF prefix <> "\" provided"+ ParseAddressRawWrongSize addr -> "Given raw address " <>+ hexF addr <> " has invalid length " <> show (length addr)+ ParseAddressRawMalformedSeparator addr -> "Given raw address " <> hexF addr <>+ " does not end with \"\\00\""++-- | Parse the given address in its raw byte form used by Tezos+-- (e.g "01521139f84791537d54575df0c74a8084cc68861c00")) . Or fail otherwise+-- if it's invalid.+parseAddressRaw :: ByteString -> Either ParseAddressRawError Address+parseAddressRaw (BS.splitAt 2 -> (prefix, address)) =+ case BS.take 1 prefix of+ "\00" -> parseKeyAddressRaw (BS.tail prefix) address+ "\01" -> parseContractAddressRaw prefix address+ _ -> Left $ ParseAddressRawInvalidPrefix prefix+ where+ parseKeyAddressRaw keyPrefix keyAddress+ | length keyAddress /= keyHashLengthBytes+ = Left $ ParseAddressRawWrongSize keyAddress+ | otherwise = do+ matchedPrefix <- case keyPrefix of+ "\00" -> Right KeyHashEd25519+ "\01" -> Right KeyHashSecp256k1+ "\02" -> Right KeyHashP256+ _ -> Left $ ParseAddressRawInvalidPrefix keyPrefix+ pure $ KeyAddress (KeyHash matchedPrefix keyAddress)++ parseContractAddressRaw contractPrefix contractAddress+ | length contractAddress /= contractHashLengthBytes+ = Left $ ParseAddressRawWrongSize contractAddress+ | BS.last contractAddress /= 0x00+ = Left $ ParseAddressRawMalformedSeparator contractAddress+ | otherwise = do+ let contractAddress' = BS.drop 1 contractPrefix <> BS.init contractAddress -- drop last "\00" of contract address+ pure $ ContractAddress (ContractHash contractAddress')+ data ParseContractAddressError = ParseContractAddressWrongBase58Check- | ParseContractAddressWrongTag ByteString- | ParseContractAddressWrongSize Int+ | ParseContractAddressWrongSize ByteString+ | ParseContractAddressWrongPrefix ByteString deriving stock (Show, Eq, Generic) instance NFData ParseContractAddressError -instance Buildable.Buildable ParseContractAddressError where+instance Buildable ParseContractAddressError where build = \case ParseContractAddressWrongBase58Check -> "Wrong base58check format"- ParseContractAddressWrongTag tag ->- "Wrong tag for a contract address: " <> fmt (hexF tag)- ParseContractAddressWrongSize s ->- "Wrong size for a contract address: " <> Buildable.build s+ ParseContractAddressWrongSize bs ->+ "Wrong size for a contract address: " <> hexF bs <>+ " (" <> build (length bs) <> ")"+ ParseContractAddressWrongPrefix prefix ->+ "Invalid prefix \"" <> hexF prefix <> "\" provided" parseContractHash :: Text -> Either ParseContractAddressError ContractHash parseContractHash text = case decodeBase58CheckWithPrefix contractAddressPrefix text of Left (B58CheckWithPrefixWrongPrefix prefix) ->- Left (ParseContractAddressWrongTag prefix)+ Left (ParseContractAddressWrongPrefix prefix) Left B58CheckWithPrefixWrongEncoding -> Left ParseContractAddressWrongBase58Check- -- We know that the length must be 20.- -- Currently it's hardcoded here, later we'll probably have a `Hash` type.- Right bs | length bs == 20 -> Right (ContractHash bs)- | otherwise -> Left $ ParseContractAddressWrongSize (length bs)+ Right bs | length bs == contractHashLengthBytes -> Right (ContractHash bs)+ | otherwise -> Left $ ParseContractAddressWrongSize bs --- | Parse a `TK` contract address, fail if address does not match+----------------------------------------------------------------------------+-- Unsafe+----------------------------------------------------------------------------++-- | Parse a @KT1@ contract address, fail if address does not match -- the expected format. unsafeParseContractHash :: HasCallStack => Text -> ContractHash-unsafeParseContractHash =- either (error . pretty) id . parseContractHash+unsafeParseContractHash = either (error . pretty) id . parseContractHash --- It's a magic constant used by Tezos to encode a contract address.--- It was deduced empirically.-contractAddressPrefix :: ByteString-contractAddressPrefix = "\2\90\121"+-- | Partial version of 'parseAddress' which assumes that the address+-- is correct. Can be used in tests.+unsafeParseAddress :: HasCallStack => Text -> Address+unsafeParseAddress = either (error . pretty) id . parseAddress +-- | Partially parse raw bytes representation of an address and assume that+-- it is correct from the beginning. Can be used in tests.+unsafeParseAddressRaw :: ByteString -> Address+unsafeParseAddressRaw = either (error . pretty) id . parseAddressRaw+ instance HasCLReader Address where getReader = eitherReader parseAddrDo where@@ -228,24 +292,6 @@ -- Aeson instances ---------------------------------------------------------------------------- -instance ToJSON ContractHash where- toJSON = Aeson.String . formatContractHash- toEncoding = Aeson.text . formatContractHash--instance ToJSONKey ContractHash where- toJSONKey = AesonTypes.toJSONKeyText formatContractHash--instance FromJSON ContractHash where- parseJSON =- Aeson.withText "ContractHash" $- either (fail . pretty) pure . parseContractHash--instance FromJSONKey ContractHash where- fromJSONKey =- AesonTypes.FromJSONKeyTextParser- (either (fail . pretty) pure . parseContractHash)-- instance ToJSON Address where toJSON = Aeson.String . formatAddress toEncoding = Aeson.text . formatAddress@@ -272,10 +318,3 @@ where genKeyAddress = KeyAddress <$> arbitrary genContractAddress = ContractAddress . ContractHash . BS.pack <$> vector 20--genAddress :: MonadGen m => m Address-genAddress = Gen.choice [genKeyAddress, genContractAddress]- where- genKeyAddress = KeyAddress <$> genKeyHash- genContractAddress = ContractAddress . ContractHash <$> Gen.bytes (Range.singleton 20)-
src/Tezos/Core.hs view
@@ -18,6 +18,8 @@ , mulMutez , divModMutez , divModMutezInt+ , zeroMutez+ , oneMutez -- * Timestamp , Timestamp (..)@@ -41,7 +43,6 @@ , mformatChainId , parseChainId , chainIdLength- , genChainId ) where import Data.Aeson (FromJSON(..), ToJSON(..))@@ -55,11 +56,7 @@ import Data.Time.Format (defaultTimeLocale, parseTimeM) import Data.Time.LocalTime (utc, utcToZonedTime) import Data.Time.RFC3339 (formatTimeRFC3339)-import Fmt (fmt, hexF, pretty)-import Formatting.Buildable (Buildable(build))-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range+import Fmt (Buildable(build), fmt, hexF, pretty) import qualified Language.Haskell.TH.Quote as TH import Language.Haskell.TH.Syntax (liftData) import qualified Options.Applicative as Opt@@ -169,6 +166,12 @@ toMutez' :: Integer -> Mutez toMutez' = Mutez . fromInteger +zeroMutez :: Mutez+zeroMutez = Mutez minBound++oneMutez :: Mutez+oneMutez = Mutez 1+ ---------------------------------------------------------------------------- -- Timestamp ----------------------------------------------------------------------------@@ -339,9 +342,6 @@ instance Arbitrary ChainId where arbitrary = ChainIdUnsafe . BS.pack <$> (vector 4)--genChainId :: MonadGen m => m ChainId-genChainId = ChainIdUnsafe <$> Gen.bytes (Range.singleton 4) ---------------------------------------------------------------------------- -- JSON
src/Tezos/Crypto.hs view
@@ -40,13 +40,6 @@ , KeyHashTag (..) , KeyHash (..) - -- * Generators- , genPublicKey- , genSecretKey- , genSignature- , genKeyHashTag- , genKeyHash- -- * Public/secret key functions , detSecretKey , toPublic@@ -54,6 +47,7 @@ -- * Signature , signatureToBytes , mkSignature+ , parseSignatureRaw , signatureLengthBytes , checkSignature @@ -62,12 +56,14 @@ , formatPublicKey , mformatPublicKey , parsePublicKey+ , parsePublicKeyRaw , formatSignature , mformatSignature , parseSignature , formatKeyHash , mformatKeyHash , parseKeyHash+ , parseKeyHashRaw , keyHashLengthBytes -- * Hashing@@ -82,17 +78,18 @@ , decodeBase58Check , B58CheckWithPrefixError (..) , decodeBase58CheckWithPrefix+ , keyDecoders+ , keyHashDecoders ) where import Data.Aeson (FromJSON(..), ToJSON(..)) import qualified Data.Aeson as Aeson import qualified Data.Aeson.Encoding as Aeson+import qualified Data.Binary.Get as Get import qualified Data.ByteArray as BA import qualified Data.ByteString as BS-import Fmt (Buildable, build, pretty)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range+import qualified Data.ByteString.Lazy as LBS+import Fmt (Buildable, build, hexF, pretty) import Test.QuickCheck (Arbitrary(..), elements, oneof) import Michelson.Text@@ -102,6 +99,7 @@ import qualified Tezos.Crypto.Secp256k1 as Secp256k1 import Tezos.Crypto.Util import Util.CLI+import Util.Binary ---------------------------------------------------------------------------- -- Types, instances, conversions@@ -123,9 +121,6 @@ instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary -genPublicKey :: MonadGen m => m PublicKey-genPublicKey = toPublic <$> genSecretKey- -- | Secret cryptographic key used by Tezos. -- Constructors correspond to 'PublicKey' constructors. data SecretKey@@ -155,13 +150,6 @@ , SecretKeyP256 <$> arbitrary ] -genSecretKey :: MonadGen m => m SecretKey-genSecretKey = Gen.choice- [ SecretKeyEd25519 <$> Ed25519.genSecretKey- , SecretKeySecp256k1 <$> Secp256k1.genSecretKey- , SecretKeyP256 <$> P256.genSecretKey- ]- -- | Create a public key from a secret key. toPublic :: SecretKey -> PublicKey toPublic = \case@@ -227,14 +215,6 @@ , SignatureGeneric . BS.replicate signatureLengthBytes <$> arbitrary ] -genSignature :: MonadGen m => m Signature-genSignature = Gen.choice- [ SignatureEd25519 <$> Ed25519.genSignature- , SignatureSecp256k1 <$> Secp256k1.genSignature- , SignatureP256 <$> P256.genSignature- , SignatureGeneric <$> Gen.bytes (Range.singleton signatureLengthBytes)- ]- ---------------------------------------------------------------------------- -- Signature ----------------------------------------------------------------------------@@ -255,6 +235,19 @@ where l = BA.length ba +parseSignatureRaw :: ByteString -> Either ParseSignatureRawError Signature+parseSignatureRaw ba = maybeToRight (ParseSignatureRawWrongSize ba) $ mkSignature ba++data ParseSignatureRawError+ = ParseSignatureRawWrongSize ByteString+ deriving stock (Eq, Show, Generic)++instance Buildable ParseSignatureRawError where+ build =+ \case+ ParseSignatureRawWrongSize ba -> "Given raw signature " <>+ hexF ba <> " has invalid length " <> show (length ba)+ -- Apparently Tezos relies on the fact that in all schemes signature -- size is 64 bytes, so it also has generic signature and always reads -- 64 bytes during unpack.@@ -278,7 +271,6 @@ genericSignatureTag = "\004\130\043" -- | Check that a sequence of bytes has been signed with a given key.--- TODO (#18) consider generic signature here as well. checkSignature :: PublicKey -> Signature -> ByteString -> Bool checkSignature pk0 sig0 bytes = case (pk0, sig0) of@@ -288,6 +280,18 @@ Secp256k1.checkSignature pk sig bytes (PublicKeyP256 pk, SignatureP256 sig) -> P256.checkSignature pk sig bytes+ (PublicKeyEd25519 pk, SignatureGeneric sBytes) ->+ Ed25519.mkSignature sBytes+ & fmap (\sig -> Ed25519.checkSignature pk sig bytes)+ & fromRight False+ (PublicKeySecp256k1 pk, SignatureGeneric sBytes) ->+ Secp256k1.mkSignature sBytes+ & fmap (\sig -> Secp256k1.checkSignature pk sig bytes)+ & fromRight False+ (PublicKeyP256 pk, SignatureGeneric sBytes) ->+ P256.mkSignature sBytes+ & fmap (\sig -> P256.checkSignature pk sig bytes)+ & fromRight False _ -> False ----------------------------------------------------------------------------@@ -314,6 +318,12 @@ , fmap PublicKeyP256 . P256.parsePublicKey ]) +parsePublicKeyRaw :: ByteString -> Either Text PublicKey+parsePublicKeyRaw ba =+ case Get.runGetOrFail (decodeWithTag "key" keyDecoders) (LBS.fromStrict ba) of+ Right (_, _, result) -> Right result+ Left (_, _, err) -> Left (toText err)+ formatSignature :: Signature -> Text formatSignature = \case SignatureEd25519 sig -> Ed25519.formatSignature sig@@ -385,9 +395,6 @@ instance Arbitrary KeyHashTag where arbitrary = elements [minBound .. ] -genKeyHashTag :: MonadGen m => m KeyHashTag-genKeyHashTag = Gen.enumBounded- instance NFData KeyHashTag -- | Blake2b_160 hash of a public key.@@ -408,9 +415,6 @@ instance Arbitrary KeyHash where arbitrary = hashKey <$> arbitrary -genKeyHash :: MonadGen m => m KeyHash-genKeyHash = hashKey <$> genPublicKey- -- | Compute the b58check of a public key hash. hashKey :: PublicKey -> KeyHash hashKey =@@ -434,11 +438,25 @@ parseKeyHash :: Text -> Either CryptoParseError KeyHash parseKeyHash txt = let+ mkKeyHash tag bs =+ KeyHash tag bs <$+ unless (length bs == keyHashLengthBytes)+ (Left $ CryptoParseUnexpectedLength "KeyHash" (length bs))+ parse :: KeyHashTag -> Either CryptoParseError KeyHash- parse tag = KeyHash tag <$> parseImpl (keyHashTagBytes tag) pure txt+ parse tag = mkKeyHash tag =<< parseImpl (keyHashTagBytes tag) pure txt in firstRight $ map parse $ minBound :| [succ minBound ..] +parseKeyHashRaw :: ByteString -> Either CryptoParseError KeyHash+parseKeyHashRaw ba =+ if (BS.length ba - 1 == keyHashLengthBytes) then+ case Get.runGetOrFail (decodeWithTag "key_hash" keyHashDecoders) (LBS.fromStrict ba) of+ Right (_, _, result) -> Right result+ Left (_, _, err) -> Left $ CryptoParseBinaryError (toText err)+ else+ Left $ CryptoParseUnexpectedLength "key_hash" (BS.length ba)+ keyHashTagBytes :: KeyHashTag -> ByteString keyHashTagBytes = \case@@ -449,3 +467,22 @@ instance HasCLReader KeyHash where getReader = eitherReader (first pretty . parseKeyHash . toText) getMetavar = "KEY_HASH"++keyDecoders :: [TaggedDecoder PublicKey]+keyDecoders =+ [ 0x00 #: decodeBytesLike "key Ed25519"+ (fmap PublicKeyEd25519 . Ed25519.mkPublicKey)+ , 0x01 #: decodeBytesLike "key Secp256k1"+ (fmap PublicKeySecp256k1 . Secp256k1.mkPublicKey)+ , 0x02 #: decodeBytesLike "key P256"+ (fmap PublicKeyP256 . P256.mkPublicKey)+ ]++keyHashDecoders :: [TaggedDecoder KeyHash]+keyHashDecoders =+ [ 0x00 #: KeyHash KeyHashEd25519 <$> getPayload+ , 0x01 #: KeyHash KeyHashSecp256k1 <$> getPayload+ , 0x02 #: KeyHash KeyHashP256 <$> getPayload+ ]+ where+ getPayload = getByteStringCopy keyHashLengthBytes
src/Tezos/Crypto/Ed25519.hs view
@@ -12,12 +12,6 @@ , detSecretKey , toPublic - -- * Generators- , genPublicKey- , genSecretKey- , genSignature-- -- * Raw bytes (no checksums, tags or anything) , publicKeyToBytes , mkPublicKey@@ -46,9 +40,6 @@ import Data.ByteArray (ByteArray, ByteArrayAccess, convert) import qualified Data.ByteString as BS import Fmt (Buildable, build)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range import Test.QuickCheck (Arbitrary(..), vector) import Michelson.Text@@ -67,9 +58,6 @@ instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary -genPublicKey :: MonadGen m => m PublicKey-genPublicKey = toPublic <$> genSecretKey- instance NFData PublicKey -- | ED25519 secret cryptographic key.@@ -86,9 +74,6 @@ instance Arbitrary SecretKey where arbitrary = detSecretKey . BS.pack <$> vector 32 -genSecretKey :: MonadGen m => m SecretKey-genSecretKey = detSecretKey <$> Gen.bytes (Range.singleton 32)- -- | Create a public key from a secret key. toPublic :: SecretKey -> PublicKey toPublic = PublicKey . Ed25519.toPublic . unSecretKey@@ -102,9 +87,6 @@ arbitrary = sign <$> arbitrary <*> (encodeUtf8 @String <$> arbitrary) instance NFData Signature--genSignature :: MonadGen m => m Signature-genSignature = sign <$> genSecretKey <*> Gen.utf8 (Range.linear 0 100) Gen.unicode ---------------------------------------------------------------------------- -- Conversion to/from raw bytes (no checksums, tags or anything)
src/Tezos/Crypto/P256.hs view
@@ -3,9 +3,6 @@ -- SPDX-License-Identifier: LicenseRef-MIT-TQ -- | P256 cryptographic primitives.------ This module is mostly a stub, it doesn't implement actual crypto.--- TODO (#18) implement crypto properly. module Tezos.Crypto.P256 ( -- * Cryptographic primitive types@@ -13,13 +10,9 @@ , SecretKey , Signature (..) , detSecretKey+ , detSecretKeyDo , toPublic - -- * Generators- , genPublicKey- , genSecretKey- , genSignature- -- * Raw bytes (no checksums, tags or anything) , publicKeyToBytes , mkPublicKey@@ -37,119 +30,113 @@ , parseSignature -- * Signing+ , sign , checkSignature ) where -import Crypto.Random (getRandomBytes)+import Crypto.Hash (Blake2b_256(..))+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Generate as ECC.Generate+import Crypto.PubKey.ECC.Types (Curve(..), CurveName(..), getCurveByName)+import Crypto.Random (MonadRandom, drgNewSeed, seedFromInteger, withDRG) import Data.ByteArray (ByteArray, ByteArrayAccess)-import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Fmt (Buildable, build)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range import Test.QuickCheck (Arbitrary(..), vector) import Michelson.Text import Tezos.Crypto.Util +curve :: Curve+curve = getCurveByName SEC_p256r1+ ---------------------------------------------------------------------------- -- Types, instances, conversions ---------------------------------------------------------------------------- -- | P256 public cryptographic key. newtype PublicKey = PublicKey- { unPublicKey :: ByteString- } deriving stock (Show, Eq, Generic)+ { unPublicKey :: ECDSA.PublicKey+ } deriving stock (Eq, Show, Generic) instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary -instance NFData PublicKey--genPublicKey :: MonadGen m => m PublicKey-genPublicKey = toPublic <$> genSecretKey+instance NFData PublicKey where+ rnf (PublicKey (ECDSA.PublicKey cu q))+ = rnfCurve cu `seq` rnf q -- | P256 secret cryptographic key. newtype SecretKey = SecretKey- { unSecretKey :: ByteString+ { unSecretKey :: ECDSA.KeyPair } deriving stock (Show, Eq, Generic) -instance NFData SecretKey+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 =- SecretKey $ deterministic seed $ getRandomBytes publicKeyLengthBytes+detSecretKey seed = deterministic seed $ detSecretKeyDo +detSecretKeyDo :: MonadRandom m => m SecretKey+detSecretKeyDo = SecretKey <$> do+ (publicKey, privateKey) <- ECC.Generate.generate curve+ return $+ ECDSA.KeyPair curve (ECDSA.public_q publicKey) (ECDSA.private_d privateKey)+ instance Arbitrary SecretKey where arbitrary = detSecretKey . BS.pack <$> vector 32 -genSecretKey :: MonadGen m => m SecretKey-genSecretKey = detSecretKey <$> Gen.bytes (Range.singleton 32)- -- | Create a public key from a secret key. toPublic :: SecretKey -> PublicKey-toPublic = PublicKey . unSecretKey+toPublic =+ PublicKey .+ ECDSA.PublicKey curve . (\(ECDSA.KeyPair _ pp _) -> pp) . unSecretKey -- | P256 cryptographic signature. newtype Signature = Signature- { unSignature :: ByteString+ { unSignature :: ECDSA.Signature } deriving stock (Show, Eq, Generic) instance Arbitrary Signature where- arbitrary = Signature . BS.pack <$> replicateM signatureLengthBytes arbitrary--instance NFData Signature+ arbitrary = do+ seed <- drgNewSeed . seedFromInteger <$> arbitrary+ byteToSign <- arbitrary+ return $ fst $ withDRG seed $ do+ sk <- detSecretKeyDo+ sign sk (one byteToSign) -genSignature :: MonadGen m => m Signature-genSignature = Signature <$> Gen.bytes (Range.singleton signatureLengthBytes)+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) ---------------------------------------------------------------------------- -- | Convert a 'PublicKey' to raw bytes.------ TODO (#18): implement properly. publicKeyToBytes :: forall ba. ByteArray ba => PublicKey -> ba-publicKeyToBytes = BA.convert . unPublicKey+publicKeyToBytes (PublicKey p) =+ publicKeyToBytes_ curve p -- | Make a 'PublicKey' from raw bytes.------ TODO (#18): implement properly. mkPublicKey :: ByteArrayAccess ba => ba -> Either CryptoParseError PublicKey-mkPublicKey ba- | l == publicKeyLengthBytes =- Right $ PublicKey (BA.convert ba)- | otherwise =- Left $ CryptoParseUnexpectedLength "public key" l- where- l = BA.length ba+mkPublicKey ba = PublicKey <$> mkPublicKey_ curve ba publicKeyLengthBytes :: Integral n => n-publicKeyLengthBytes = 33+publicKeyLengthBytes = publicKeyLengthBytes_ curve -- | Convert a 'PublicKey' to raw bytes.------ TODO (#18): implement properly. signatureToBytes :: ByteArray ba => Signature -> ba-signatureToBytes = BA.convert . unSignature+signatureToBytes (Signature sig) =+ signatureToBytes_ curve sig -- | Make a 'Signature' from raw bytes.------ TODO (#18): implement properly. mkSignature :: ByteArray ba => ba -> Either CryptoParseError Signature-mkSignature ba- | l == signatureLengthBytes =- Right $ Signature (BA.convert ba)- | otherwise =- Left $ CryptoParseUnexpectedLength "signature" l- where- l = BA.length ba+mkSignature ba = Signature <$> mkSignature_ curve ba signatureLengthBytes :: Integral n => n-signatureLengthBytes = 64+signatureLengthBytes = signatureLengthBytes_ curve ---------------------------------------------------------------------------- -- Magic bytes@@ -193,6 +180,12 @@ -- Signing ---------------------------------------------------------------------------- +-- | Sign a message using the secret key.+sign :: MonadRandom m => SecretKey -> ByteString -> m Signature+sign (SecretKey keyPair) =+ fmap Signature . ECDSA.sign (ECDSA.toPrivateKey keyPair) Blake2b_256+ -- | Check that a sequence of bytes has been signed with a given key. checkSignature :: PublicKey -> Signature -> ByteString -> Bool-checkSignature _ _ _ = False+checkSignature (PublicKey pk) (Signature sig) =+ ECDSA.verify Blake2b_256 pk sig
src/Tezos/Crypto/Secp256k1.hs view
@@ -10,13 +10,9 @@ , SecretKey , Signature (..) , detSecretKey+ , detSecretKeyDo , toPublic - -- * Generators- , genPublicKey- , genSecretKey- , genSignature- -- * Raw bytes (no checksums, tags or anything) , publicKeyToBytes , mkPublicKey@@ -39,20 +35,13 @@ ) where import Crypto.Hash (Blake2b_256(..))-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(..), CurveCommon(..), CurveName(..), CurvePrime(..), Point(..), curveSizeBits,- getCurveByName)+import Crypto.PubKey.ECC.Types (Curve(..), CurveName(..), getCurveByName) import Crypto.Random (MonadRandom, drgNewSeed, seedFromInteger, withDRG) import Data.ByteArray (ByteArray, ByteArrayAccess)-import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Fmt (Buildable, build)-import Hedgehog (MonadGen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range import Test.QuickCheck (Arbitrary(..), vector) import Michelson.Text@@ -61,45 +50,21 @@ curve :: Curve curve = getCurveByName SEC_p256k1 -curveSizeBytes :: Int-curveSizeBytes = curveSizeBits curve `div` 8- ---------------------------------------------------------------------------- -- Types, instances, conversions ---------------------------------------------------------------------------- -- | Secp256k1 public cryptographic key.-data PublicKey = PublicKey+newtype PublicKey = PublicKey { unPublicKey :: ECDSA.PublicKey- , pkBytes :: Maybe ByteString- -- ^ This is the hack we use to make serialization correct.- -- Decoding is currently not implemented, so when we have to- -- decode bytes we remember these bytes and produce some random- -- public key.- --- -- TODO (#18) remove it.- } deriving stock (Show, Generic)---- TODO (#18): derive it instead once the above hack is removed.-instance Eq PublicKey where- pk1 == pk2 = publicKeyToBytes @ByteString pk1 == publicKeyToBytes pk2+ } deriving stock (Eq, Show, Generic) instance Arbitrary PublicKey where arbitrary = toPublic <$> arbitrary -genPublicKey :: MonadGen m => m PublicKey-genPublicKey = toPublic <$> genSecretKey--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)+ rnf (PublicKey (ECDSA.PublicKey cu q))+ = rnfCurve cu `seq` rnf q -- | Secp256k1 secret cryptographic key. newtype SecretKey = SecretKey@@ -123,13 +88,10 @@ instance Arbitrary SecretKey where arbitrary = detSecretKey . BS.pack <$> vector 32 -genSecretKey :: MonadGen m => m SecretKey-genSecretKey = detSecretKey <$> Gen.bytes (Range.singleton 32)- -- | Create a public key from a secret key. toPublic :: SecretKey -> PublicKey toPublic =- flip PublicKey Nothing .+ PublicKey . ECDSA.PublicKey curve . (\(ECDSA.KeyPair _ pp _) -> pp) . unSecretKey -- | Secp256k1 cryptographic signature.@@ -148,81 +110,33 @@ instance NFData Signature where rnf (Signature (ECDSA.Signature a b)) = rnf a `seq` rnf b -genSignature :: MonadGen m => m Signature-genSignature = do- seed <- drgNewSeed . seedFromInteger <$> Gen.integral (Range.linearFrom 0 -1000 1000)- byteToSign <- Gen.word8 Range.linearBounded- return $ fst $ withDRG seed $ do- sk <- detSecretKeyDo- sign sk (one byteToSign)- ---------------------------------------------------------------------------- -- Conversion to/from raw bytes (no checksums, tags or anything) ---------------------------------------------------------------------------- -- | Convert a 'PublicKey' to raw bytes.------ TODO (#18): apparently it uses compressed SEC format as described in--- <https://www.oreilly.com/library/view/programming-bitcoin/9781492031482/ch04.html>--- However, it is not tested yet. publicKeyToBytes :: forall ba. ByteArray ba => PublicKey -> ba-publicKeyToBytes (PublicKey _ (Just bytes)) = BA.convert bytes-publicKeyToBytes (PublicKey (ECDSA.PublicKey _ publicPoint) Nothing) =- case publicPoint of- Point x y -> prefix y `BA.append` coordToBytes x- PointO -> error "PublicKey somehow contains infinity point"- where- prefix :: Integer -> ba- prefix y- | odd y = BA.singleton 0x03- | otherwise = BA.singleton 0x02+publicKeyToBytes (PublicKey p) =+ publicKeyToBytes_ curve p -- | Make a 'PublicKey' from raw bytes.------ TODO (#18): it should decode from compressed SEC format, but it's left--- for a future task, so for now we return a constant. mkPublicKey :: ByteArrayAccess ba => ba -> Either CryptoParseError PublicKey-mkPublicKey ba- | l == publicKeyLengthBytes =- Right $ PublicKey (ECDSA.PublicKey curve $ Point 11 12) (Just $ BA.convert ba)- | otherwise =- Left $ CryptoParseUnexpectedLength "public key" l- where- l = BA.length ba+mkPublicKey ba = PublicKey <$> mkPublicKey_ curve ba publicKeyLengthBytes :: Integral n => n-publicKeyLengthBytes = fromIntegral $ 1 + curveSizeBytes+publicKeyLengthBytes = publicKeyLengthBytes_ curve -- | Convert a 'PublicKey' to raw bytes.------ TODO (#18): apparently a signature always has 64 bytes, so this--- format might be correct, but it is not tested. signatureToBytes :: ByteArray ba => Signature -> ba-signatureToBytes (Signature (ECDSA.Signature r s)) =- coordToBytes r <> coordToBytes s+signatureToBytes (Signature sig) =+ signatureToBytes_ curve sig -- | Make a 'Signature' from raw bytes.------ TODO (#18): apparently a signature always has 64 bytes, so this--- format might be correct, but it is not tested. mkSignature :: ByteArray ba => ba -> Either CryptoParseError Signature-mkSignature ba- | l == signatureLengthBytes- , (rBytes, sBytes) <- BA.splitAt curveSizeBytes ba =- Right $ Signature (ECDSA.Signature (os2ip rBytes) (os2ip sBytes))- | otherwise =- Left $ CryptoParseUnexpectedLength "signature" l- where- l = BA.length ba+mkSignature ba = Signature <$> mkSignature_ curve ba signatureLengthBytes :: Integral n => n-signatureLengthBytes = fromIntegral $ curveSizeBytes + curveSizeBytes---- TODO (#18): maybe this function doesn't make sense.--- We are using `i2ospOf_` because `curveSizeBits` ensures that--- the number won't have more than that many bytes.-coordToBytes :: ByteArray ba => Integer -> ba-coordToBytes = i2ospOf_ curveSizeBytes+signatureLengthBytes = signatureLengthBytes_ curve ---------------------------------------------------------------------------- -- Magic bytes@@ -273,5 +187,5 @@ -- | Check that a sequence of bytes has been signed with a given key. checkSignature :: PublicKey -> Signature -> ByteString -> Bool-checkSignature (PublicKey pk _) (Signature sig) =+checkSignature (PublicKey pk) (Signature sig) = ECDSA.verify Blake2b_256 pk sig
src/Tezos/Crypto/Util.hs view
@@ -14,17 +14,33 @@ , parseImpl , firstRight , deterministic++ -- * ECDSA Utils+ , rnfCurve+ , publicKeyLengthBytes_+ , mkSignature_+ , signatureToBytes_+ , mkPublicKey_+ , publicKeyToBytes_+ , signatureLengthBytes_ ) where import Crypto.Error (CryptoError)-import Crypto.Number.Serialize (os2ip)+import Crypto.Number.Serialize (i2ospOf_, os2ip)+import Crypto.Number.ModArithmetic (squareRoot) import Crypto.Random (ChaChaDRG, MonadPseudoRandom, drgNewSeed, seedFromInteger, withDRG)-import qualified Data.ByteArray as ByteArray+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import Crypto.PubKey.ECC.Types+ (Curve(..), CurveCommon(..), CurvePrime(..), Point(..), curveSizeBits)+import qualified Data.Binary.Get as Get+import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import qualified Data.ByteString.Base58 as Base58-import Fmt (Buildable, Builder, build, fmt, hexF)+import qualified Data.ByteString.Lazy as LBS+import Fmt (Buildable, Builder, (+|), (|+), build, fmt, hexF) import Tezos.Crypto.Hash+import Util.Binary (getRemainingByteStringCopy) -- | Error that can happen during parsing of cryptographic primitive types. data CryptoParseError@@ -32,6 +48,7 @@ | CryptoParseWrongTag ByteString | CryptoParseCryptoError CryptoError | CryptoParseUnexpectedLength Builder Int+ | CryptoParseBinaryError Text deriving stock (Show, Eq) instance NFData CryptoParseError where@@ -47,6 +64,7 @@ build (displayException err) CryptoParseUnexpectedLength what l -> "Unexpected length of " <> what <> ": " <> build l+ CryptoParseBinaryError err -> "" +| err |+ "" -- | Encode a bytestring in Base58Check format. encodeBase58Check :: ByteString -> Text@@ -87,8 +105,8 @@ else Left (B58CheckWithPrefixWrongPrefix actualPrefix) -- | Template for 'format*' functions.-formatImpl :: ByteArray.ByteArrayAccess x => ByteString -> x -> Text-formatImpl tag = encodeBase58Check . mappend tag . ByteArray.convert+formatImpl :: BA.ByteArrayAccess x => ByteString -> x -> Text+formatImpl tag = encodeBase58Check . mappend tag . BA.convert -- | Template for 'parse*' functions. parseImpl@@ -118,3 +136,99 @@ deterministic seed = fst . withDRG chachaSeed where chachaSeed = drgNewSeed . seedFromInteger . os2ip $ seed++---------------------------------------------------------+-- Utilities shared by @Secp256k1@ and @P256@.+---------------------------------------------------------++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)++curveSizeBytes :: Curve -> Int+curveSizeBytes curve = curveSizeBits curve `div` 8++signatureLengthBytes_ :: Integral n => Curve -> n+signatureLengthBytes_ curve = fromIntegral $ (curveSizeBytes curve) + (curveSizeBytes curve)++coordToBytes :: BA.ByteArray ba => Curve -> Integer -> ba+coordToBytes curve = i2ospOf_ (curveSizeBytes curve)++publicKeyLengthBytes_ :: Integral n => Curve -> n+publicKeyLengthBytes_ curve = fromIntegral $ 1 + (curveSizeBytes curve)++-- | Make a 'PublicKey' from raw bytes.+--+-- Raw bytes are in the format of Compressed SEC Format. Refer to this article on how this is parsed:+-- <https://www.oreilly.com/library/view/programming-bitcoin/9781492031482/ch04.html>+--+mkPublicKey_ :: BA.ByteArrayAccess ba => Curve -> ba -> Either CryptoParseError ECDSA.PublicKey+mkPublicKey_ curve ba+ | l == (publicKeyLengthBytes_ curve) = do+ (isYEven, x) <- toCryptoEither $ Get.runGetOrFail getX+ (LBS.fromStrict $ BA.convert ba)+ (p, a, b) <- fromCurveFP curve+ let alpha = x ^ (3 :: Integer) + a * x + b+ beta <- squareRoot p alpha+ & maybeToRight (CryptoParseBinaryError "Could not find square root.")+ let (evenBeta, oddBeta) =+ if (beta `mod` 2 == 0) then+ (beta, p - beta)+ else+ (p - beta, beta)+ let y = if isYEven then evenBeta+ else oddBeta+ pure $ ECDSA.PublicKey curve $ Point x y+ | otherwise =+ Left $ CryptoParseUnexpectedLength "public key" l+ where+ l = BA.length ba++ getX :: Get.Get (Bool, Integer)+ getX = do+ yPrefix <- Get.getWord8+ xBytes <- getRemainingByteStringCopy+ return (even yPrefix, os2ip xBytes)++ fromCurveFP :: Curve -> Either CryptoParseError (Integer, Integer, Integer)+ fromCurveFP = \case+ CurveFP (CurvePrime p (CurveCommon a b _ _ _)) -> Right (p, a, b)+ CurveF2m _ -> Left $ CryptoParseBinaryError+ "Should not happen. Expect `curve` to be `CurveFP` but got `CurveF2m` instead."++ toCryptoEither :: Either (_a, _b, String) (_c, _d, a) -> Either CryptoParseError a+ toCryptoEither g =+ case g of+ Right (_, _, a) -> Right a+ Left (_, _, err) -> Left $ CryptoParseBinaryError $ toText err++-- | Convert a 'PublicKey' to raw bytes.+publicKeyToBytes_ :: forall ba. (BA.ByteArray ba, HasCallStack) => Curve -> ECDSA.PublicKey -> ba+publicKeyToBytes_ curve (ECDSA.PublicKey _ publicPoint) =+ case publicPoint of+ Point x y -> prefix y `BA.append` coordToBytes curve x+ PointO -> error "PublicKey somehow contains infinity point"+ where+ prefix :: Integer -> ba+ prefix y+ | odd y = BA.singleton 0x03+ | otherwise = BA.singleton 0x02++-- | Convert a 'PublicKey' to raw bytes.+signatureToBytes_ :: BA.ByteArray ba => Curve -> ECDSA.Signature -> ba+signatureToBytes_ curve (ECDSA.Signature r s) =+ coordToBytes curve r <> coordToBytes curve s++-- | Make a 'Signature' from raw bytes.+mkSignature_ :: BA.ByteArray ba => Curve -> ba -> Either CryptoParseError ECDSA.Signature+mkSignature_ curve ba+ | l == (signatureLengthBytes_ curve)+ , (rBytes, sBytes) <- BA.splitAt (curveSizeBytes curve) ba =+ Right $ ECDSA.Signature (os2ip rBytes) (os2ip sBytes)+ | otherwise =+ Left $ CryptoParseUnexpectedLength "signature" l+ where+ l = BA.length ba
src/Util/Binary.hs view
@@ -8,14 +8,23 @@ ( UnpackError (..) , ensureEnd , launchGet+ , TaggedDecoder(..)+ , (#:)+ , decodeBytesLike+ , decodeWithTag+ , getByteStringCopy+ , getRemainingByteStringCopy+ , unknownTag ) where import Prelude hiding (EQ, Ordering(..), get) import Data.Binary (Get) import qualified Data.Binary.Get as Get+import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS-import Fmt (Buildable, build, pretty, (+|), (+||), (|+), (||+))+import qualified Data.List as List+import Fmt (Buildable, build, fmt, hexF, pretty, (+|), (+||), (|+), (||+)) import Text.Hex (encodeHex) ----------------------------------------------------------------------------@@ -45,3 +54,60 @@ case Get.runGetOrFail decoder bs of Left (_remainder, _offset, err) -> Left . UnpackError $ toText err Right (_remainder, _offset, res) -> Right res++-- | Describes how 'decodeWithTag' should decode tag-dependent data.+-- We expect bytes of such structure: 'tdTag' followed by a bytestring+-- which will be parsed with 'tdDecoder'.+data TaggedDecoder a = TaggedDecoder+ { tdTag :: Word8+ , tdDecoder :: Get a+ }++-- | Alias for 'TaggedDecoder' constructor.+(#:) :: Word8 -> Get a -> TaggedDecoder a+(#:) = TaggedDecoder+infixr 0 #:++-- | Get a bytestring of the given length leaving no references to the+-- original data in serialized form.+getByteStringCopy :: Int -> Get ByteString+getByteStringCopy = fmap BS.copy . Get.getByteString++-- | Get remaining available bytes.+--+-- Note that reading all remaining decoded input may be expensive and is thus+-- discouraged, use can use this function only when you know that amount+-- of data to be consumed is limited, e.g. within 'decodeAsBytes' call.+getRemainingByteStringCopy :: Get ByteString+getRemainingByteStringCopy = do+ lbs <- Get.getRemainingLazyByteString+ -- Avoiding memory overflows in case bad length to 'Get.isolate' was provided.+ -- Normally this function is used only to decode primitives, 'Signature' in+ -- the worst case, so we could set little length, but since this is a hack+ -- anyway let's make sure it never obstructs our work.+ when (length lbs > 640000) $ fail "Too big length for an entity"+ return (LBS.toStrict lbs)++-- | Fail with "unknown tag" error.+unknownTag :: String -> Word8 -> Get a+unknownTag desc tag =+ fail . fmt $ "Unknown " <> build desc <> " tag: 0x" <> hexF tag++-- Common decoder for the case when packed data starts with a tag (1+-- byte) that specifies how to decode remaining data.+decodeWithTag :: String -> [TaggedDecoder a] -> Get a+decodeWithTag what decoders = do+ tag <- Get.label (what <> " tag") Get.getWord8+ -- Number of decoders is usually small, so linear runtime lookup should be ok.+ case List.find ((tag ==) . tdTag) decoders of+ Nothing -> unknownTag what tag+ Just TaggedDecoder{..} -> tdDecoder++decodeBytesLike+ :: (Buildable err)+ => String -> (ByteString -> Either err a) -> Get a+decodeBytesLike what constructor = do+ bs <- getRemainingByteStringCopy+ case constructor bs of+ Left err -> fail $ "Wrong " +| what |+ ": " +| err |+ ""+ Right res -> pure res
+ src/Util/CustomGeneric.hs view
@@ -0,0 +1,381 @@+-- SPDX-FileCopyrightText: 2020 Tocqueville Group+--+-- SPDX-License-Identifier: LicenseRef-MIT-TQ+--+-- | Generic deriving with unbalanced trees.++module Util.CustomGeneric+ ( -- * Custom Generic strategies+ withDepths+ , rightBalanced+ , leftBalanced+ , rightComb+ , leftComb+ -- * Depth usage helpers+ , cstr+ , fld+ -- * Instance derivation+ , customGeneric+ ) where++import qualified GHC.Generics as G+import Generics.Deriving.TH (makeRep0Inline)+import Language.Haskell.TH++----------------------------------------------------------------------------+-- Simple type synonyms+----------------------------------------------------------------------------++-- | Simple tuple specifying the depth of a constuctor and a list of depths+-- for its fields.+--+-- This is used as a way to specify the tree topology of the Generic instance+-- to derive.+type CstrDepth = (Natural, [Natural])++-- | Simple tuple that defines the "shape" of a constructor: it's name and number+-- of fields. Used only in this module.+type CstrShape = (Name, Int)++-- | Type of a strategy to derive 'G.Generic' instances, it will be given the actual+-- 'CstrShape's for a data-type and needs to return the 'CstrDepth's for it.+-- It should when possible make checks and 'fail', using the constructors' 'Name'+-- provided by the 'CstrShape'.+type GenericStrategy = [CstrShape] -> Q [CstrDepth]++-- | Simple type synonym used (internally) between functions, basically extending+-- 'CstrDepth' with the 'Name's of the constructor and its fields.+type NamedCstrDepths = (Natural, Name, [(Natural, Name)])++----------------------------------------------------------------------------+-- Custom Generic strategies+----------------------------------------------------------------------------++-- | In this strategy the desired depths of contructors (in the type tree) and+-- fields (in each constructor's tree) are provided manually and simply checked+-- against the number of actual constructors and fields.+withDepths :: [CstrDepth] -> GenericStrategy+withDepths treeDepths cstrShape = do+ when (length treeDepths /= length cstrShape) $ fail+ "Number of contructors' depths does not match number of data contructors."+ forM_ (zip (map snd treeDepths) cstrShape) $ \(fDepths, (constrName, fldNum)) ->+ when (length fDepths /= fldNum) . fail $+ "Number of fields' depths does not match number of field for data " <>+ "constructor: " <> show constrName+ return treeDepths++-- | Strategy to make right-balanced instances (both in constructors and fields).+rightBalanced :: GenericStrategy+rightBalanced = fromDepthsStrategy makeRightBalDepths++-- | Strategy to make left-balanced instances (both in constructors and fields).+leftBalanced :: GenericStrategy+leftBalanced = fromDepthsStrategy (reverse . makeRightBalDepths)++-- | Strategy to make fully right-leaning instances (both in constructors and fields).+rightComb :: GenericStrategy+rightComb = fromDepthsStrategy (reverse . makeLeftCombDepths)++-- | Strategy to make fully left-leaning instances (both in constructors and fields).+leftComb :: GenericStrategy+leftComb = fromDepthsStrategy makeLeftCombDepths++----------------------------------------------------------------------------+-- Generic strategies' builders+----------------------------------------------------------------------------++-- | Helper to make a strategy that created depths for constructor and fields+-- in the same way, just from their number.+fromDepthsStrategy :: (Int -> [Natural]) -> GenericStrategy+fromDepthsStrategy dStrategy cShapes = return $+ zip (dStrategy $ length cShapes) $ map (dStrategy . snd) cShapes++makeRightBalDepths :: Int -> [Natural]+makeRightBalDepths n = foldr (const addRightBalDepth) [] [1..n]+ where+ addRightBalDepth :: [Natural] -> [Natural]+ addRightBalDepth = \case+ [] -> [0]+ [x] -> [x + 1, x + 1]+ (x : y : xs) | x == y -> x : addRightBalDepth (x : xs)+ (_ : y : xs) -> y : y : y : xs++makeLeftCombDepths :: Int -> [Natural]+makeLeftCombDepths 0 = []+makeLeftCombDepths n = map fromIntegral $ (n - 1) : [n - 1, n - 2..1]++----------------------------------------------------------------------------+-- Depth usage helpers+----------------------------------------------------------------------------++-- | Helper for making a constructor depth.+--+-- Note that this is only intended to be more readable than directly using a+-- tuple with 'withDepths' and for the ability to be used in places where+-- @RebindableSyntax@ overrides the number literal resolution.+cstr :: forall n. KnownNat n => [Natural] -> CstrDepth+cstr flds = (natVal (Proxy @n), flds)++-- | Helper for making a field depth.+--+-- Note that this is only intended to be more readable than directly using a+-- tuple with 'withDepths' and for the ability to be used in places where+-- @RebindableSyntax@ overrides the number literal resolution.+fld :: forall n. KnownNat n => Natural+fld = natVal $ Proxy @n++----------------------------------------------------------------------------+-- Instance derivation+----------------------------------------------------------------------------++-- | Derives the 'G.Generic' instance for a type given its name and a+-- 'GenericStrategy' to use.+--+-- The strategy is used to calculate the depths of the data-type constructors+-- and each constructors' fields.+--+-- The depths are used to generate the tree of the 'G.Generic' representation,+-- allowing for a custom one, in contrast with the one derived automatically.+--+-- This only supports "plain" @data@ types (no GADTs, no @newtype@s, etc.) and+-- requires the depths to describe a fully and well-defined tree (see 'unbalancedFold').+--+-- For example, this is valid (and uses the 'withDepths' strategy with the 'cstr'+-- and 'fld' helpers) and results in a balanced instance, equivalent to the+-- auto-derived one:+--+-- @@@+-- data CustomType a+-- = CustomUp Integer Integer+-- | CustomMid {unMid :: Natural}+-- | CustomDown a+-- | CustomNone+--+-- $(customGeneric "CustomType" $ withDepths+-- [ cstr @2 [fld @1, fld @1]+-- , cstr @2 [fld @0]+-- , cstr @2 [fld @0]+-- , cstr @2 []+-- ])+-- @@@+--+-- and this is a valid, but fully left-leaning one:+--+-- @@@+-- $(customGeneric "CustomType" $ withDepths+-- [ cstr @3 [fld @1, fld @1]+-- , cstr @3 [fld @0]+-- , cstr @2 [fld @0]+-- , cstr @1 []+-- ])+-- @@@+--+-- and, just as a demonstration, this is the same fully left-leaning one, but+-- made using the simpler 'leftComb' strategy:+--+-- @@@+-- $(customGeneric "CustomType" leftComb)+-- @@@+--+customGeneric :: String -> GenericStrategy -> Q [Dec]+customGeneric typeStr genStrategy = do+ -- reify the data type+ (typeName, mKind, vars, constructors) <- reifyDataType typeStr+ -- obtain info about its constructor and desired tree+ let derivedType = deriveFullType typeName mKind vars+ cShapes <- cstrShapes constructors+ treeDepths <- genStrategy cShapes+ weightedConstrs <- makeWeightedConstrs treeDepths cShapes+ -- produce the Generic instance+ res <- instanceD (pure []) (appT (conT ''G.Generic) derivedType)+ [ tySynInstD . tySynEqn Nothing (appT (conT ''G.Rep) derivedType) $+ makeUnbalancedRep typeName treeDepths derivedType+ , makeUnbalancedFrom weightedConstrs+ , makeUnbalancedTo weightedConstrs+ ]+ return [res]++-- | Reifies info from a type name (given as a 'String').+-- The lookup happens from the current splice's scope (see 'lookupTypeName') and+-- the only accepted result is a "plain" data type (no GADTs).+reifyDataType :: String -> Q (Name, Maybe Kind, [TyVarBndr], [Con])+reifyDataType typeStr = do+ typeInfo <- lookupTypeName typeStr >>= \case+ Nothing -> fail $ "Failed type name lookup for: '" <> typeStr <> "'."+ Just tn -> reify tn+ case typeInfo of+ TyConI (DataD _ typeName vars mKind constrs _) ->+ return (typeName, mKind, vars, constrs)+ _ -> fail $+ "Only plain datatypes are supported for derivation, but '" <>+ typeStr <> "' instead reifies to:\n" <> show typeInfo++-- | Derives, as well as possible, a type definition from its name, its kind+-- (where known) and its variables.+deriveFullType :: Name -> Maybe Kind -> [TyVarBndr] -> TypeQ+deriveFullType tName mKind = addTypeSig . foldl appT (conT tName) . makeVarsType+ where+ addTypeSig :: TypeQ -> TypeQ+ addTypeSig = flip sigT $ fromMaybe StarT mKind++ makeVarsType :: [TyVarBndr] -> [TypeQ]+ makeVarsType = map $ \case+ PlainTV vName -> varT vName+ KindedTV vName kind -> sigT (varT vName) kind++-- | Calculate the "shape" for each of the given constructors.+-- The shape is simply the 'Name' of the constructor and the number of its args.+cstrShapes :: [Con] -> Q [CstrShape]+cstrShapes constructors = forM constructors $ \case+ NormalC name lst -> return (name, length lst)+ RecC name lst -> return (name, length lst)+ InfixC _ name _ -> return (name, 2)+ constr -> fail $ "Unsupported constructor: " <> show constr++-- | Combines depths with constructors, 'fail'ing in case of mismatches, and+-- generates 'Name's for the constructors' arguments.+makeWeightedConstrs :: [CstrDepth] -> [CstrShape] -> Q [NamedCstrDepths]+makeWeightedConstrs treeDepths cSizes = do+ forM (zip treeDepths cSizes) $ \((cDepth, fDepths), (cName, fNum)) -> do+ constrVarsNames <- replicateM fNum $ newName "v"+ return (cDepth, cName, zip fDepths constrVarsNames)++-- | Creates the 'G.Rep' type for an unbalanced 'G.Generic' instance, for a type+-- given its name, constructors' depths and derived full type.+--+-- Note: given that these types definition can be very complex to generate,+-- especially in the metadata, here we let @generic-deriving@ make a balanced+-- value first (see 'makeRep0Inline') and then de-balance the result.+makeUnbalancedRep :: Name -> [CstrDepth] -> TypeQ -> TypeQ+makeUnbalancedRep typeName treeDepths derivedType = do+ -- let generic-deriving create the balanced type first+ balRep <- makeRep0Inline typeName derivedType+ -- separate the top-most type metadata from the constructors' trees+ (typeMd, constrTypes) <- dismantleGenericTree [t| (G.:+:) |] [t| G.C1 |] balRep+ -- for each of the constructor's trees+ unbalConstrs <- forM (zip constrTypes treeDepths) $ \(constrType, treeDepth) ->+ case treeDepth of+ (n, []) ->+ -- when there are no fields there is no tree to unbalance+ return (n, constrType)+ (n, fields) -> do+ -- separate the top-most constructor metadata from the fields' trees+ (constrMd, fieldTypes) <- dismantleGenericTree [t| (G.:*:) |] [t| G.S1 |] constrType+ -- build the unbalanced tree of fields+ unbalConstRes <- unbalancedFold (zip fields fieldTypes) (appT . appT (conT ''(G.:*:)))+ -- return the new unbalanced constructor+ return (n, AppT constrMd unbalConstRes)+ -- build the unbalanced tree of constructors and rebuild the type+ appT (pure typeMd) $ unbalancedFold unbalConstrs (appT . appT (conT ''(G.:+:)))++-- | Breaks down a tree of @Generic@ types from the contructor of "nodes" and+-- the constructor for "leaves" metadata.+--+-- This expects (and should always be the case) the "root" to be a @Generic@+-- metadata contructor, which is returned in the result alongside the list of+-- leaves (in order).+dismantleGenericTree :: TypeQ -> TypeQ -> Type -> Q (Type, [Type])+dismantleGenericTree nodeConstrQ leafMetaQ (AppT meta nodes) = do+ nodeConstr <- nodeConstrQ+ leafMeta <- leafMetaQ+ let collectLeafsTypes :: Type -> [Type]+ collectLeafsTypes tp@(AppT a b) = case a of+ AppT md _ | md == leafMeta -> [tp]+ nd | nd == nodeConstr -> collectLeafsTypes b+ _ -> collectLeafsTypes a <> collectLeafsTypes b+ collectLeafsTypes x = error $+ "Unexpected lack of Generic constructor application: " <> show x+ return (meta, collectLeafsTypes nodes)+dismantleGenericTree _ _ x = error $+ "Unexpected lack of Generic Metadata: " <> show x++-- | Create the unbalanced 'G.from' fuction declaration for a type starting from+-- its list of weighted constructors.+makeUnbalancedFrom :: [NamedCstrDepths] -> DecQ+makeUnbalancedFrom wConstrs = do+ (cPatts, cDepthExp) <- fmap unzip . forM wConstrs $ \(cDepth, cName, wFields) -> do+ (fPatts, fDepthExp) <- fmap unzip . forM wFields $ \(fDepth, fName) -> do+ -- make pattern for field variable+ fPat <- varP fName+ -- make expression to asseble a Generic Field from its variable+ fExpr <- appE [| G.M1 |] . appE [| G.K1 |] $ varE fName+ return (fPat, (fDepth, fExpr))+ -- make pattern for this constructor+ let cPatt = ConP cName fPatts+ -- make expression to assemble its fields as an isolated Generic Constructor+ cExp <- appE [| G.M1 |] $ case fDepthExp of+ [] -> conE 'G.U1+ _ -> unbalancedFold fDepthExp (appE . appE [| (G.:*:) |])+ return (cPatt, (cDepth, [cExp]))+ -- make expressions to assemble all Generic Constructors+ cExps <- mapQ (appE [| G.M1 |]) $ unbalancedFold cDepthExp $ \xs ys ->+ (<>) <$> mapQ (appE [| G.L1 |]) xs <*> mapQ (appE [| G.R1 |]) ys+ -- make function definition+ funD 'G.from $ zipWith (\p e -> clause [pure p] (normalB $ pure e) []) cPatts cExps++-- | Create the unbalanced 'G.to' fuction declaration for a type starting from+-- its list of weighted constructors.+makeUnbalancedTo :: [NamedCstrDepths] -> DecQ+makeUnbalancedTo wConstrs = do+ (cExps, cDepthPat) <- fmap unzip . forM wConstrs $ \(cDepth, cName, wFields) -> do+ (fExps, fDepthPat) <- fmap unzip . forM wFields $ \(fDepth, fName) -> do+ -- make expression for field variable+ fExp <- varE fName+ -- make pattern for a Generic Field from its variable+ fPatt <- conP1 'G.M1 . conP1 'G.K1 $ varP fName+ return (fExp, (fDepth, fPatt))+ -- make pattern for this isolated Generic Constructor+ cPatt <- conP1 'G.M1 $ case fDepthPat of+ [] -> conP 'G.U1 []+ _ -> unbalancedFold fDepthPat (conP2 '(G.:*:))+ -- make expression to assemble this constructor+ let cExp = foldl AppE (ConE cName) fExps+ return (cExp, (cDepth, [cPatt]))+ -- make patterns for all Generic Constructors+ cPatts <- mapQ (conP1 'G.M1) $ unbalancedFold cDepthPat $ \xs ys ->+ (<>) <$> mapQ (conP1 'G.L1) xs <*> mapQ (conP1 'G.R1) ys+ -- make function definition+ funD 'G.to $ zipWith (\p e -> clause [pure p] (normalB $ pure e) []) cPatts cExps++-- | Recursively aggregates the values in the given list by merging (with the+-- given function) the ones that are adjacent and with the same depth.+--+-- This will fail for every case in which the list cannot be folded into a single+-- 0-depth value.+unbalancedFold :: forall a. Eq a => [(Natural, a)] -> (Q a -> Q a -> Q a) -> Q a+unbalancedFold lst f = unbalancedFoldRec lst >>= \case+ [(0, result)] -> return result+ [(n, _)] -> fail $+ "Resulting unbalanced tree has a single root, but of depth " <> show n <>+ " instead of 0. Check your depths definitions."+ _ -> fail $+ "Cannot create a tree from nodes of depths: " <> show (map fst lst) <>+ ". Check your depths definitions."+ where+ unbalancedFoldRec :: [(Natural, a)] -> Q [(Natural, a)]+ unbalancedFoldRec xs = do+ ys <- unbalancedFoldSingle xs+ if xs == ys then return xs else unbalancedFoldRec ys++ unbalancedFoldSingle :: [(Natural, a)] -> Q [(Natural, a)]+ unbalancedFoldSingle = \case+ [] -> return []+ (dx, x) : (dy, y) : xs | dx == dy -> do+ dxy <- f (pure x) (pure y)+ return $ (dx - 1, dxy) : xs+ x : xs -> do+ ys <- unbalancedFoldSingle xs+ return (x : ys)++----------------------------------------------------------------------------+-- Utility functions+----------------------------------------------------------------------------++conP1 :: Name -> PatQ -> PatQ+conP1 name pat = conP name [pat]++conP2 :: Name -> PatQ -> PatQ -> PatQ+conP2 name pat1 pat2 = conP name [pat1, pat2]++mapQ :: (Q a -> Q a) -> Q [a] -> Q [a]+mapQ f qlst = qlst >>= mapM (f . pure)
src/Util/Exception.hs view
@@ -10,6 +10,7 @@ TextException (..) -- * Better printing of exceptions+ , DisplayExceptionInShow(..) , displayUncaughtException ) where @@ -43,7 +44,7 @@ -- Better printing of exceptions at executable-level ---------------------------------------------------------------------------- -newtype DisplayExceptionInShow = DisplayExceptionInShow SomeException+newtype DisplayExceptionInShow = DisplayExceptionInShow { unDisplayExceptionInShow :: SomeException } instance Show DisplayExceptionInShow where show (DisplayExceptionInShow se) = displayException se
src/Util/Named.hs view
@@ -25,6 +25,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@@ -35,9 +36,11 @@ (<.!>) :: Functor m => Name name -> m a -> m (NamedF Identity a name) (<.!>) name = fmap (name .!)+infixl 4 <.!> (<.?>) :: Functor m => Name name -> m (Maybe a) -> m (NamedF Maybe a name) (<.?>) name = fmap (name .?)+infixl 4 <.?> type family ApplyNamedFunctor (f :: Kind.Type -> Kind.Type) (a :: Kind.Type) where ApplyNamedFunctor Identity a = a@@ -64,14 +67,6 @@ ---------------------------------------------------------------------------- -- Instances ------------------------------------------------------------------------------instance Wrapped (NamedF Identity a name) where- type Unwrapped (NamedF Identity a name) = a- _Wrapped' = iso (\(ArgF a) -> runIdentity a) (ArgF . Identity)--instance Wrapped (NamedF Maybe a name) where- type Unwrapped (NamedF Maybe a name) = Maybe a- _Wrapped' = iso (\(ArgF a) -> a) ArgF deriving stock instance Eq (f a) => Eq (NamedF f a name) deriving stock instance Ord (f a) => Ord (NamedF f a name)
src/Util/Test/Arbitrary.hs view
@@ -11,7 +11,7 @@ import Prelude hiding (EQ, GT, LT) import Test.QuickCheck- (Arbitrary(..), Gen, choose, frequency, oneof, resize, suchThatMap, vector)+ (Arbitrary(..), Gen, choose, elements, frequency, oneof, resize, suchThatMap, vector) import Test.QuickCheck.Arbitrary.ADT (ToADTArbitrary(..)) import Test.QuickCheck.Gen (unGen) import Test.QuickCheck.Instances.ByteString ()@@ -21,9 +21,8 @@ import Test.QuickCheck.Random (mkQCGen) import Michelson.ErrorPos (InstrCallStack(..), LetName(..), Pos(..), SrcPos(..))-import Michelson.Test () import Michelson.Untyped- (Annotation, Contract'(..), Elt(..), ExpandedExtInstr, ExpandedOp(..),+ (Annotation, Contract'(..), Elt(..), EntriesOrder(..), ExpandedExtInstr, ExpandedOp(..), ExtInstrAbstract(..), InstrAbstract(..), InternalByteString(..), ParameterType(..), StackTypePattern(..), T(..), TyVar(..), Type(..), Value'(..), Var(..), mkAnnotation) import Tezos.Core (Mutez(..))@@ -81,9 +80,12 @@ smallList1 :: Arbitrary a => Gen (NonEmpty a) smallList1 = smallList `suchThatMap` nonEmpty +instance Arbitrary EntriesOrder where+ arbitrary = elements [minBound .. maxBound]+ instance (Arbitrary op, ToADTArbitrary op) => ToADTArbitrary (Contract' op) instance (Arbitrary op) => Arbitrary (Contract' op) where- arbitrary = Contract <$> arbitrary <*> arbitrary <*> smallList+ arbitrary = Contract <$> arbitrary <*> arbitrary <*> smallList <*> arbitrary instance (Arbitrary op, ToADTArbitrary op, Arbitrary (ExtInstrAbstract op)) => ToADTArbitrary (InstrAbstract op) instance (Arbitrary op, Arbitrary (ExtInstrAbstract op)) => Arbitrary (InstrAbstract op) where
− src/Util/Test/Gen.hs
@@ -1,317 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Util.Test.Gen- ( genInternalByteString- , genVar- , genTyVar- , genExpandedOp- , genInstrCallStack- , genSrcPos- , genPos- , genLetName- , genExtInstrAbstract- , genPrintComment- , genStackRef- , genTestAssert- , genStackFn- , genStackTypePattern- , genInstrAbstract- , genContract- , genContract'- , genValue- , genValue'- , genElt- , genParameterType- , genType- , genEpName- , genAnnotation- , genT- ) where--import Prelude hiding (EQ, GT, LT)--import qualified Data.Text as T-import Hedgehog (MonadGen(GenBase), Range)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range--import Michelson.ErrorPos (InstrCallStack(..), LetName(..), Pos(..), SrcPos(..))-import Michelson.Test.Util (genEither)-import Michelson.Text (genMText)-import Michelson.Untyped- (Annotation, Contract, Contract'(..), Elt(..), EpName, ExpandedOp(..), ExtInstrAbstract(..),- InstrAbstract(..), InternalByteString(..), ParameterType(..), PrintComment(..), StackFn(..),- StackRef(..), StackTypePattern(..), T(..), TestAssert(..), TyVar(..), Type(..), Value,- Value'(..), Var(..), epNameFromRefAnn, mkAnnotation, specialFieldAnn, specialVarAnns)--genInternalByteString :: MonadGen m => m InternalByteString-genInternalByteString = InternalByteString <$> Gen.bytes (Range.linear 0 100)--genVar :: MonadGen m => m Var-genVar = Var <$> genSmallText--genTyVar :: (MonadGen m, GenBase m ~ Identity) => m TyVar-genTyVar = Gen.choice [VarID <$> genVar, TyCon <$> genType]--genExpandedOp :: (MonadGen m, GenBase m ~ Identity) => m ExpandedOp-genExpandedOp = Gen.recursive Gen.choice- -- non-recursive constructors- [- -- NB: When used together, genExpandedOp & genInstrAbstract are mutually recursive.- -- So we use a generator of InstrAbstract that does not contain any ops as an escape hatch.- PrimEx <$> Gen.choice instrAbstractWithoutOp- ]- -- recursive constructors- [ PrimEx <$> genInstrAbstract genExpandedOp- , SeqEx <$> genSmallList genExpandedOp- , Gen.subtermM genExpandedOp $ \expandedOp -> WithSrcEx <$> genInstrCallStack <*> pure expandedOp- ]--genInstrCallStack :: MonadGen m => m InstrCallStack-genInstrCallStack = InstrCallStack <$> genLetCallStack <*> genSrcPos- where- genLetCallStack = Gen.frequency- [ (80, pure [])- , (18, Gen.list (Range.singleton 1) genLetName)- , (2, Gen.list (Range.singleton 2) genLetName)- ]--genSrcPos :: MonadGen m => m SrcPos-genSrcPos = SrcPos <$> genPos <*> genPos--genPos :: MonadGen m => m Pos-genPos = Pos <$> Gen.word Range.linearBounded--genLetName :: MonadGen m => m LetName-genLetName = LetName <$> Gen.text (Range.linear 0 3) Gen.unicodeAll--genExtInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (ExtInstrAbstract op)-genExtInstrAbstract genOp = Gen.choice- [ STACKTYPE <$> genStackTypePattern- , FN <$> genSmallText <*> genStackFn <*> genSmallList genOp- , UTEST_ASSERT <$> genTestAssert genOp- , UPRINT <$> genPrintComment- , UCOMMENT <$> genSmallText- ]--genPrintComment :: MonadGen m => m PrintComment-genPrintComment = PrintComment <$> Gen.list (Range.linear 0 5) (genEither genSmallText genStackRef)--genStackRef :: MonadGen m => m StackRef-genStackRef = StackRef <$> Gen.integral (Range.linear 0 (fromIntegral $ maxBound @Word64))--genTestAssert :: MonadGen m => m op -> m (TestAssert op)-genTestAssert genOp = TestAssert <$> genSmallText <*> genPrintComment <*> genSmallList genOp--genStackFn :: (MonadGen m, GenBase m ~ Identity) => m StackFn-genStackFn = StackFn- <$> Gen.maybe (Gen.set smallCollectionRange genVar)- <*> genStackTypePattern- <*> genStackTypePattern--genStackTypePattern :: (MonadGen m, GenBase m ~ Identity) => m StackTypePattern-genStackTypePattern = Gen.recursive Gen.choice- [ pure StkEmpty, pure StkRest ]- [ Gen.subtermM genStackTypePattern $ \stp -> StkCons <$> genTyVar <*> pure stp ]--genInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (InstrAbstract op)-genInstrAbstract genOp =- Gen.choice $ instrAbstractWithOp genOp <> instrAbstractWithoutOp--instrAbstractWithOp :: (MonadGen m, GenBase m ~ Identity) => m op -> [m (InstrAbstract op)]-instrAbstractWithOp genOp =- [ EXT <$> genExtInstrAbstract genOp- , PUSH <$> genAnnotation <*> genType <*> genValue' genOp- , IF_NONE <$> genSmallList genOp <*> genSmallList genOp- , IF_LEFT <$> genSmallList genOp <*> genSmallList genOp- , IF_CONS <$> genSmallList genOp <*> genSmallList genOp- , MAP <$> genAnnotation <*> genSmallList genOp- , ITER <$> genSmallList genOp- , IF <$> genSmallList genOp <*> genSmallList genOp- , LOOP <$> genSmallList genOp- , LOOP_LEFT <$> genSmallList genOp- , LAMBDA <$> genAnnotation <*> genType <*> genType <*> genSmallList genOp- , DIP <$> genSmallList genOp- , DIPN <$> Gen.word Range.linearBounded <*> genSmallList genOp- , CREATE_CONTRACT <$> genAnnotation <*> genAnnotation <*> genContract' genOp- ]--instrAbstractWithoutOp :: (MonadGen m, GenBase m ~ Identity) => [m (InstrAbstract op)]-instrAbstractWithoutOp =- [ DROPN <$> Gen.word Range.linearBounded- , pure DROP- , DUP <$> genAnnotation- , pure SWAP- , DIG <$> Gen.word Range.linearBounded- , DUG <$> Gen.word Range.linearBounded- , SOME <$> genAnnotation <*> genAnnotation- , NONE <$> genAnnotation <*> genAnnotation <*> genType- , UNIT <$> genAnnotation <*> genAnnotation- , PAIR <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation- , CAR <$> genAnnotation <*> genAnnotation- , CDR <$> genAnnotation <*> genAnnotation- , LEFT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType- , RIGHT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType- , NIL <$> genAnnotation <*> genAnnotation <*> genType- , CONS <$> genAnnotation- , SIZE <$> genAnnotation- , EMPTY_SET <$> genAnnotation <*> genAnnotation <*> genType- , EMPTY_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType- , EMPTY_BIG_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType- , MEM <$> genAnnotation- , GET <$> genAnnotation- , UPDATE <$> genAnnotation- , EXEC <$> genAnnotation- , APPLY <$> genAnnotation- , pure FAILWITH- , CAST <$> genAnnotation <*> genType- , RENAME <$> genAnnotation- , PACK <$> genAnnotation- , UNPACK <$> genAnnotation <*> genAnnotation <*> genType- , CONCAT <$> genAnnotation- , SLICE <$> genAnnotation- , ISNAT <$> genAnnotation- , ADD <$> genAnnotation- , SUB <$> genAnnotation- , MUL <$> genAnnotation- , EDIV <$> genAnnotation- , ABS <$> genAnnotation- , NEG <$> genAnnotation- , LSL <$> genAnnotation- , LSR <$> genAnnotation- , OR <$> genAnnotation- , AND <$> genAnnotation- , XOR <$> genAnnotation- , NOT <$> genAnnotation- , COMPARE <$> genAnnotation- , EQ <$> genAnnotation- , NEQ <$> genAnnotation- , LT <$> genAnnotation- , GT <$> genAnnotation- , LE <$> genAnnotation- , GE <$> genAnnotation- , INT <$> genAnnotation- , SELF <$> genAnnotation <*> genAnnotation- , CONTRACT <$> genAnnotation <*> genAnnotation <*> genType- , TRANSFER_TOKENS <$> genAnnotation- , SET_DELEGATE <$> genAnnotation- , IMPLICIT_ACCOUNT <$> genAnnotation- , NOW <$> genAnnotation- , AMOUNT <$> genAnnotation- , BALANCE <$> genAnnotation- , CHECK_SIGNATURE <$> genAnnotation- , SHA256 <$> genAnnotation- , SHA512 <$> genAnnotation- , BLAKE2B <$> genAnnotation- , HASH_KEY <$> genAnnotation- , SOURCE <$> genAnnotation- , SENDER <$> genAnnotation- , ADDRESS <$> genAnnotation- , CHAIN_ID <$> genAnnotation- ]--genContract :: (MonadGen m, GenBase m ~ Identity) => m Contract-genContract = genContract' genExpandedOp--genContract' :: (MonadGen m, GenBase m ~ Identity) => m op -> m (Contract' op)-genContract' genOp = Contract <$> genParameterType <*> genType <*> genSmallList genOp--genValue :: (MonadGen m, GenBase m ~ Identity) => m Value-genValue = genValue' genExpandedOp--genValue' :: MonadGen m => m op -> m (Value' op)-genValue' genOp = Gen.recursive Gen.choice- -- non-recursive constructors- [ ValueInt <$> Gen.integral (Range.linearFrom 0 (fromIntegral $ minBound @Int64) (fromIntegral $ maxBound @Word64))- , ValueString <$> genMText- , ValueBytes <$> genInternalByteString- , pure ValueUnit- , pure ValueTrue- , pure ValueFalse- , pure ValueNone- , pure ValueNil- ]- -- recursive constructors- [ Gen.subterm2 (genValue' genOp) (genValue' genOp) ValuePair- , Gen.subterm (genValue' genOp) ValueLeft- , Gen.subterm (genValue' genOp) ValueRight- , Gen.subterm (genValue' genOp) ValueSome- , ValueSeq <$> genSmallNonEmpty (genValue' genOp)- , ValueMap <$> genSmallNonEmpty (genElt genOp)- , ValueLambda <$> genSmallNonEmpty genOp- ]--genElt :: MonadGen m => m op -> m (Elt op)-genElt genOp = Elt <$> genValue' genOp <*> genValue' genOp--genParameterType :: (MonadGen m, GenBase m ~ Identity) => m ParameterType-genParameterType = ParameterType <$> genType <*> genAnnotation---genType :: (MonadGen m, GenBase m ~ Identity) => m Type-genType = Type <$> genT <*> genAnnotation--genEpName :: (MonadGen m, GenBase m ~ Identity) => m EpName-genEpName = Gen.mapMaybe (rightToMaybe . epNameFromRefAnn) genAnnotation--genAnnotation :: forall m a. (MonadGen m, GenBase m ~ Identity) => m (Annotation a)-genAnnotation = Gen.mapMaybe (rightToMaybe . mkAnnotation) genAnnotationText- where- genAnnotationText :: m Text- genAnnotationText = Gen.frequency- [ (1, pure "")- , (17, T.cons <$> annStart <*> Gen.text (Range.linear 0 100) annBodyChar)- , (1, pure specialFieldAnn)- , (1, Gen.element specialVarAnns)- ]-- annStart :: m Char- annStart = Gen.choice [ pure '_', Gen.alphaNum ]-- annBodyChar :: m Char- annBodyChar = Gen.choice [ annStart, Gen.element (".%@" :: String) ]--genT :: (MonadGen m, GenBase m ~ Identity) => m T-genT =- Gen.recursive Gen.choice- -- non-recursive constructors- [ pure TKey- , pure TUnit- , pure TSignature- , pure TChainId- , pure TOperation- , pure TInt- , pure TNat- , pure TString- , pure TBytes- , pure TMutez- , pure TBool- , pure TKeyHash- , pure TTimestamp- , pure TAddress- ]- -- recursive constructors- [ TOption <$> genType- , TList <$> genType- , TSet <$> genType- , TContract <$> genType- , TPair <$> genAnnotation <*> genAnnotation <*> genType <*> genType- , TOr <$> genAnnotation <*> genAnnotation <*> genType <*> genType- , TLambda <$> genType <*> genType- , TMap <$> genType <*> genType- , TBigMap <$> genType <*> genType- ]--smallCollectionRange :: Range Int-smallCollectionRange = Range.linear 0 3--genSmallList :: MonadGen m => m a -> m [a]-genSmallList = Gen.list smallCollectionRange--genSmallNonEmpty :: MonadGen m => m a -> m (NonEmpty a)-genSmallNonEmpty = Gen.nonEmpty smallCollectionRange--genSmallText :: MonadGen m => m Text-genSmallText = Gen.text (Range.linear 0 10) Gen.unicodeAll
− src/Util/Test/Ingredients.hs
@@ -1,18 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Ingridients that we use in our test suite.--module Util.Test.Ingredients- ( ourIngredients- ) where--import Test.Tasty.Ingredients (Ingredient, composeReporters)-import Test.Tasty.Ingredients.Basic (consoleTestReporter, listingTests)-import Test.Tasty.Runners.AntXML (antXMLRunner)---- | This is the default set of ingredients extended with the--- 'antXMLRunner' which is used to generate xml reports for CI.-ourIngredients :: [Ingredient]-ourIngredients = [listingTests, antXMLRunner `composeReporters` consoleTestReporter]
− test/Main.hs
@@ -1,16 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Main- ( main- ) where--import Test.Tasty (defaultMainWithIngredients)--import Util.Test.Ingredients (ourIngredients)--import Tree (tests)--main :: IO ()-main = tests >>= defaultMainWithIngredients ourIngredients
− test/Test/Analyzer.hs
@@ -1,56 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for analyzer.--module Test.Analyzer- ( unit_Sample_analyze- ) where--import Prelude hiding (EQ)--import qualified Data.HashMap.Strict as HM-import Test.HUnit (Assertion, (@?=))--import Michelson.Analyzer-import Michelson.Text-import qualified Michelson.Typed as T-import Michelson.Typed.Instr--unit_Sample_analyze :: Assertion-unit_Sample_analyze = analyze sample @?= expectedRes- where- expectedRes = AnalyzerRes- { arConstStrings = HM.fromList [(str1, 3), (str3, 4)]- , arConstBytes = mempty- , arErrorTags = HM.fromList [(str1, 2), (str3, 1)]- }--str1 :: MText-str1 = [mt|aa|]--str3 :: MText-str3 = [mt|bb|]--sample :: T.ContractCode 'T.TString 'T.TString-sample =- CAR `Seq` DUP `Seq`- pushStr str3 `Seq`- CONCAT `Seq`- SIZE `Seq` INT `Seq` EQ `Seq`- IF (LAMBDA (T.VLam . T.RfNormal $ pushStr str3 `Seq` CONCAT) `Seq` DROP)- (DIP (pushStr str1) `Seq` DROP) `Seq`- DIP (pushStr str3) `Seq` DROP `Seq`- pushTrue `Seq`- IF (pushStr str1 `Seq` FAILWITH)- pushTrue `Seq`- IF (Seq NOW $ Seq DROP $ pushStr str1 `Seq` (PAIR `Seq` FAILWITH))- Nop `Seq`- PUSH (T.VPair (T.VString str3, T.VUnit)) `Seq` FAILWITH- where- pushStr :: forall s. MText -> Instr s ('T.TString ': s)- pushStr str = PUSH (T.VString str)-- pushTrue :: forall s. Instr s ('T.TBool ': s)- pushTrue = PUSH (T.VBool True)
− test/Test/EntryPoints.hs
@@ -1,96 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# LANGUAGE DeriveAnyClass #-}---- | Tests for Lorentz compilation which uses 'LorentzCompilationWay'.-module Test.EntryPoints- ( test_EpAddress- , test_ParamNotes- , test_ParamEpError- ) where--import Prelude hiding (or)--import Data.Default (def)-import Fmt (pretty)-import Test.HUnit (assertBool, (@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Michelson.Test.Util (runGen)-import Michelson.Typed-import Michelson.Untyped (ann, noAnn, unsafeBuildEpName)-import Tezos.Address--import Test.Util.Hedgehog--test_EpAddress :: [TestTree]-test_EpAddress =- [ testGroup "Parsing" $- [ testCase "Simple entrypoint" $- parseEpAddress (formatAddress addr <> "%ab")- @?= Right (EpAddress addr (unsafeBuildEpName "ab"))-- , testCase "No entrypoint" $- parseEpAddress (formatAddress addr)- @?= Right (EpAddress addr def)-- , testCase "Weird entrypoint" $- parseEpAddress (formatAddress addr <> "%a%b")- @?= Right (EpAddress addr (unsafeBuildEpName "a%b"))- ]-- , testGroup "parse . format = pure"- [ roundtripTreeSTB genEpAddress formatEpAddress parseEpAddress ]- ]- where- addr = runGen 20 123 genAddress--test_ParamNotes :: [TestTree]-test_ParamNotes =- [ testGroup "Duplications are handled" $- [ testCase "One duplicated entrypoint" $- mkParamNotes (or "a" "a" prim prim) noAnn- @?= Left (ParamEpDuplicatedNames (unsafeBuildEpName "a" :| []))- , testCase "Several duplicated entrypoint" $- mkParamNotes (or "" "" (or "a" "b" prim prim) (or "b" "a" prim prim)) noAnn- @?= Left (ParamEpDuplicatedNames (unsafeBuildEpName "a" :| [unsafeBuildEpName "b"]))- , testCase "Duplicated default entrypoint" $- mkParamNotes (or "default" "default" prim prim) noAnn- @?= Left (ParamEpDuplicatedNames (DefEpName :| []))- ]-- , testGroup "All entrypoints callable check" $- [ testCase "Non-callable entrypoint is detected in simple case" $- mkParamNotes (or "default" "" prim (or "" "q" prim prim)) noAnn- @?= Left (ParamEpUncallableArm [AcRight, AcLeft])- , testCase "Non-callable entrypoint is detected in complex case" $- mkParamNotes (or "a" "" prim (or "" "default" (or "b" "" prim prim) prim)) noAnn- @?= Left (ParamEpUncallableArm [AcRight, AcLeft, AcRight])-- , testCase "Having all leaves named is enough for callability" $- mkParamNotes (or "default" "" prim (or "q" "" prim (or "a" "b" prim prim))) noAnn- & assertBool "All arms should've considered callable" . isRight- , testCase "Having all leaves named is enough for callability" $- mkParamNotes (or "default" "a" prim (or "" "" prim (or "" "" prim prim))) noAnn- & assertBool "All arms should've considered callable" . isRight- ]- ]- where- or a1 a2 = NTOr noAnn (ann a1) (ann a2)- prim = NTKey noAnn--test_ParamEpError :: [TestTree]-test_ParamEpError =- [ testGroup "Buildable instance"- [ testCase "Duplicated entrypoints error" $- pretty @_ @Text (ParamEpDuplicatedNames $ unsafeBuildEpName "a" :| [DefEpName])- @?= "Duplicated entrypoint names: 'a', '<default>'"- , testCase "Uncallable arms error" $- pretty @_ @Text (ParamEpUncallableArm [AcLeft, AcRight])- @?= "Due to presence of 'default' entrypoint, one of contract \"arms\" \- \cannot be called: \"left - right\" (in top-to-bottom order)"- ]- ]
− test/Test/Ext.hs
@@ -1,101 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Ext- ( test_PRINT_and_TEST_ASSERT- , test_STACKTYPE- ) where--import Data.Default (def)-import Test.Hspec.Expectations (shouldSatisfy)-import Test.HUnit (Assertion, assertFailure)-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)--import Michelson.Interpret (InterpreterState(..), MorleyLogs(..), interpret)-import Michelson.Test (concatTestTrees, testTreesWithTypedContract)-import Michelson.Test.Dummy (dummyContractEnv)-import Michelson.TypeCheck (HST(..), SomeHST(..), runTypeCheck, typeCheckExt, typeCheckList, getWTP)-import Michelson.Typed (pattern AsUType, epcPrimitive)-import qualified Michelson.Typed as T-import Michelson.Untyped- (ExpandedExtInstr, ExtInstrAbstract(..), StackTypePattern(..), ParameterType(..),- T(..), TyVar(..), Type(..), ann, noAnn)--import Test.Util.Contracts--test_PRINT_and_TEST_ASSERT :: IO [TestTree]-test_PRINT_and_TEST_ASSERT = concatTestTrees- [ testTreesWithTypedContract (inContractsDir "testassert_square.mtz") $- testAssertSquareImpl- , testTreesWithTypedContract (inContractsDir "testassert_square2.mtz") $- testAssertSquareImpl- ]- where- testAssertSquareImpl c = pure- [ testCase "TEST_ASSERT assertion passed" $ do- runTest True c 100 100- runTest True c 1 1- , testCase "TEST_ASSERT assertion failed" $ do- runTest False c 0 100- runTest False c -1 -2- ]-- runTest corr contract x y = do- let x' = T.VInt x :: T.Value 'T.TInt- let y' = T.VInt y :: T.Value 'T.TInt- let area' = T.VInt $ x * y :: T.Value 'T.TInt- 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 (T.cCode contract) epcPrimitive (T.VPair (x', y')) T.VUnit dummyContractEnv `shouldSatisfy` check--test_STACKTYPE :: [TestTree]-test_STACKTYPE =- [ testCase "Correct test on [] pattern" $ runExtTest test1 True- , testCase "Correct test on [a, b] pattern" $ runExtTest test2 True- , testCase "Correct test on [a, b, ...] pattern" $ runExtTest test3 True- , testCase "Correct test on [a, b, ...] pattern and stack [a, b]" $ runExtTest test4 True-- , testCase "Failed test on [] pattern and stack [a]" $ runExtTest test5 False- , testCase "Failed test on [a, b] pattern and stack [a, b, c]" $ runExtTest test6 False- , testCase "Failed test on [a, b] pattern and stack [a]" $ runExtTest test7 False- , testCase "Failed test on [a, b, ...] pattern and stack [a]" $ runExtTest test8 False- , testCase "Failed test on [a, b] pattern and stack [a, c]" $ runExtTest test9 False- ]- where- p2 = StkCons (TyCon t1) (StkCons (TyCon t2) StkEmpty)- p3 = StkCons (TyCon t1) (StkCons (TyCon t2) StkRest)-- test1 = (STACKTYPE StkEmpty, convertToHST [])- test2 = (STACKTYPE p2, convertToHST [t1, t2])- test3 = (STACKTYPE p3, convertToHST [t1, t2, t3])- test4 = (STACKTYPE p3, convertToHST [t1, t2])-- test5 = (STACKTYPE StkEmpty, convertToHST [t1])- test6 = (STACKTYPE p2, convertToHST [t1, t2, t3])- test7 = (STACKTYPE p2, convertToHST [t1])- test8 = (STACKTYPE p3, convertToHST [t1])- test9 = (STACKTYPE p2, convertToHST [t1, t3])-- t1 = Type (TOption (Type TKey (ann "key"))) (ann "opt")- t2 = Type (TPair (ann "f") (ann "s") (Type TUnit "x") (Type TSignature "s")) noAnn- t3 = Type TInt (ann "tint")-- convertToHST :: [Type] -> SomeHST- convertToHST [] = SomeHST SNil- convertToHST (AsUType (nt :: T.Notes t1) : ts) =- case convertToHST ts of- SomeHST is -> case getWTP @t1 of- Just T.Dict -> SomeHST ((nt, T.Dict, noAnn) ::& is)- Nothing -> error "Type is not well typed"-- nh (ni, si) =- runTypeCheck (ParameterType (Type TKey noAnn) noAnn) mempty $ usingReaderT def $ typeCheckExt typeCheckList ni si-- runExtTest :: (ExpandedExtInstr, SomeHST) -> Bool -> Assertion- runExtTest (ui, SomeHST hst) correct = case (nh (ui, hst), correct) of- (Right _, False) -> assertFailure $ "Test expected to fail but it passed"- (Left e, True) -> assertFailure $ "Test expected to pass but it failed with error: " <> show e- _ -> pass
− test/Test/Integrational.hs
@@ -1,36 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for integrational testing machinery.-module Test.Integrational- ( spec_Chain_id- ) where--import Test.Hspec (Spec, it)--import Michelson.Test-import Michelson.Typed-import Tezos.Core-import Util.Named--spec_Chain_id :: Spec-spec_Chain_id = do- it "Chain id can be set" $- integrationalTestExpectation $ do- let code = DROP `Seq` CHAIN_ID `Seq` NIL `Seq` PAIR- let contract = Contract- { cCode = code- , cParamNotes = starParamNotes @'TUnit- , cStoreNotes = starNotes- }- let chainId = mkChainIdUnsafe "aaaa"-- contractAddr <-- tOriginate contract "" (toVal dummyChainId) (toMutez 50)-- setChainId chainId- tTransfer (#from .! genesisAddress) (#to .! contractAddr)- minBound DefEpName (toVal ())-- tExpectStorageConst contractAddr (toVal chainId)
− test/Test/Interpreter.hs
@@ -1,333 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Interpreter- ( test_basic5- , test_increment- , test_fail- , test_mutez_add_overflow- , test_mutez_sub_overflow- , test_basic1- , test_lsl- , test_lsr- , test_FAILWITH- , test_gas_exhaustion- , test_add1_list- , test_Sum_types- , test_Product_types- , test_split_bytes- , test_split_string_simple- , test_complex_strings- , test_contract_instr_on_implicit- , test_map_preserve_stack- ) where--import Fmt (pretty)-import Hedgehog (MonadTest, annotate, assert, forAll, property, (===))-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.Hspec.Expectations (Expectation, shouldSatisfy)-import Test.HUnit (assertFailure)-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.Interpret (MichelsonFailed(..), interpret)-import Michelson.Test (concatTestTrees, testTreesWithTypedContract)-import Michelson.Test.Dummy (dummyContractEnv)-import Michelson.Test.Unit-import Michelson.Test.Util (failedTest)-import Michelson.Text-import Michelson.Typed (IsoValue(..), T(..), epcPrimitive)-import qualified Michelson.Typed as T-import Tezos.Address-import Tezos.Crypto--import Test.Util.Contracts--interpretSimple- :: forall cp st.- ( IsoValue cp, IsoValue st- , T.ParameterScope (ToT cp)- , T.ForbidOr (ToT cp)- )- => T.Contract (ToT cp) (ToT st)- -> cp- -> st- -> ContractReturn (ToT st)-interpretSimple contract cp st =- interpret (T.cCode contract) epcPrimitive (toVal cp) (toVal st) dummyContractEnv--test_basic5 :: IO [TestTree]-test_basic5 =- testTreesWithTypedContract (contractsDir </> "basic5.tz") $ \contract -> pure- [ testCase "Basic test" $- contractProp @() @[Integer] contract (validateStorageIs [13 :: Integer, 100])- dummyContractEnv () [1]- ]--test_increment :: IO [TestTree]-test_increment =- testTreesWithTypedContract (contractsDir </> "increment.tz") $ \contract -> pure- [ testCase "Basic test" $- contractProp @() @Integer contract (validateStorageIs @Integer 24)- dummyContractEnv () 23- ]--test_fail :: IO [TestTree]-test_fail =- testTreesWithTypedContract (contractsDir </> "tezos_examples/macros/fail.tz") $ \contract -> pure- [ testCase "Fail test" $- interpretSimple contract () ()- `shouldSatisfy` (isLeft . fst)- ]--test_mutez_add_overflow :: IO [TestTree]-test_mutez_add_overflow =- testTreesWithTypedContract (contractsDir </> "mutez_add_overflow.tz") $ \contract -> pure- [ testCase "Mutez add overflow test" $- interpretSimple contract () ()- `shouldSatisfy` (isLeft . fst)- ]--test_mutez_sub_overflow :: IO [TestTree]-test_mutez_sub_overflow =- testTreesWithTypedContract (contractsDir </> "mutez_sub_underflow.tz") $ \contract -> pure- [ testCase "Mutez sub underflow test" $- interpretSimple contract () ()- `shouldSatisfy` (isLeft . fst)- ]--test_basic1 :: IO [TestTree]-test_basic1 =- testTreesWithTypedContract (contractsDir </> "basic1.tz") $ \contract -> pure- [ testProperty "Random check" $ property $ do- input <- forAll $ Gen.list (Range.linear 0 100) (Gen.integral (Range.linearFrom 0 -1000 1000))- contractProp @_ @[Integer] contract (validateBasic1 input)- dummyContractEnv () input- ]--test_lsl :: IO [TestTree]-test_lsl =- 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" $- interpretSimple contract (5 :: Natural) (257 :: Natural)- `shouldSatisfy` (isLeft . fst)- ]--test_lsr :: IO [TestTree]-test_lsr =- 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" $- interpretSimple contract (1000 :: Natural) (257 :: Natural)- `shouldSatisfy` (isLeft . fst)- ]--test_FAILWITH :: IO [TestTree]-test_FAILWITH = concatTestTrees- [ 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 (contractsDir </> "failwith_message2.tz") $ \contract ->- pure- [ testCase "Conditional failwith message test" $ do- let msg = [mt|An error occurred.|]- contractProp contract (validateMichelsonFailsWith msg) dummyContractEnv- (True, msg) ()-- , testCase "Conditional success test" $ do- let param = (False, [mt|Err|] :: MText)- contractProp contract validateSuccess dummyContractEnv param ()- ]- ]--test_gas_exhaustion :: IO [TestTree]-test_gas_exhaustion =- testTreesWithTypedContract (contractsDir </> "gas_exhaustion.tz") $ \contract -> pure- [ testCase "Contract should fail due to gas exhaustion" $ do- 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"- ]--test_add1_list :: IO [TestTree]-test_add1_list =- testTreesWithTypedContract (contractsDir </> "tezos_examples/attic/add1_list.tz") $ \contract ->- let- doValidate :: MonadTest m =>- [Integer] -> ContractPropValidator (ToT [Integer]) (m ())- doValidate param (res, _) =- case res of- Left failed -> failedTest $- "add1_list unexpectedly failed: " <> pretty failed- Right (fromVal . snd -> finalStorage) ->- map succ param === finalStorage- in pure- [ testProperty "Random check" $ property $ do- param <- forAll $ Gen.list (Range.linear 0 100) (Gen.integral (Range.linearFrom 0 -1000 1000))- contractProp contract (doValidate param) dummyContractEnv param param- ]--test_Sum_types :: IO [TestTree]-test_Sum_types = concatTestTrees- [ testTreesWithTypedContract (contractsDir </> "union.mtz") $ \contract -> pure- [ testGroup "union.mtz: union corresponds to Haskell types properly" $- let caseTest param =- contractProp contract validateSuccess dummyContractEnv param ()- in- [ testCase "Case 1" $ caseTest (Case1 3)- , testCase "Case 2" $ caseTest (Case2 [mt|a|])- , testCase "Case 3" $ caseTest (Case3 $ Just [mt|b|])- , testCase "Case 4" $ caseTest (Case4 $ Left [mt|b|])- , testCase "Case 5" $ caseTest (Case5 [[mt|q|]])- ]- ]- , testTreesWithTypedContract (contractsDir </> "case.mtz") $ \contract -> pure- [ testGroup "CASE instruction" $- let caseTest param expectedStorage =- contractProp contract (validateStorageIs @MText expectedStorage)- dummyContractEnv param [mt||]- in- [ testCase "Case 1" $ caseTest (Case1 5) [mt|int|]- , testCase "Case 2" $ caseTest (Case2 [mt|a|]) [mt|string|]- , testCase "Case 3" $ caseTest (Case3 $ Just [mt|aa|]) [mt|aa|]- , testCase "Case 4" $ caseTest (Case4 $ Right [mt|b|]) [mt|or string string|]- , testCase "Case 5" $ caseTest (Case5 $ [[mt|a|], [mt|b|]]) [mt|ab|]- ]- ]- , 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||]- ]- ]--test_Product_types :: IO [TestTree]-test_Product_types = concatTestTrees- [ 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 (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 (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)- dummyContractEnv () (0, [mt||], Nothing, Right [mt||], [])- ]- ]--test_split_bytes :: IO [TestTree]-test_split_bytes =- 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- ("\11\12\13" :: ByteString) ([] :: [ByteString])- ]--test_split_string_simple :: IO [TestTree]-test_split_string_simple =- testTreesWithTypedContract (contractsDir </> "split_string_simple.tz") $ \contract ->- pure- [ testCase "applies SLICE instruction" $ do- let- oneTest :: Natural -> Natural -> MText -> Maybe MText -> Expectation- oneTest o l str expected =- contractProp contract (validateStorageIs expected) dummyContractEnv- (o, l) (Just str)-- -- These values have been tested using alphanet.sh- oneTest 0 0 [mt|aaa|] (Just [mt||])- oneTest 2 0 [mt|aaa|] (Just [mt||])- oneTest 3 0 [mt|aaa|] Nothing- oneTest 0 5 [mt|aaa|] Nothing- oneTest 1 2 [mt|abc|] (Just [mt|bc|])- oneTest 1 1 [mt|abc|] (Just [mt|b|])- oneTest 2 1 [mt|abc|] (Just [mt|c|])- oneTest 2 2 [mt|abc|] Nothing- oneTest 1 1 [mt|a""|] (Just [mt|"|])- oneTest 1 2 [mt|a\n|] Nothing- ]--test_complex_strings :: IO [TestTree]-test_complex_strings =- testTreesWithTypedContract (contractsDir </> "complex_strings.tz") $ \contract ->- pure- [ testCase "ComplexString" $- contractProp contract- (validateStorageIs [mt|text: "aa" \\\n|])- dummyContractEnv [mt|text: |] [mt||]- ]--data Union1- = Case1 Integer- | Case2 MText- | Case3 (Maybe MText)- | Case4 (Either MText MText)- | Case5 [MText]- deriving stock (Generic)- deriving anyclass (IsoValue)--type Tuple1 = (Integer, MText, Maybe MText, Either MText MText, [MText])--test_contract_instr_on_implicit :: IO [TestTree]-test_contract_instr_on_implicit =- 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 ()-- , testCase "CONTRACT instruction considers implicit accounts as unit-parametrized" $- contractProp contractBad (validateMichelsonFailsWith [mt|No such contract|])- dummyContractEnv addr ()- ]- where- addr = mkKeyAddress . toPublic $ detSecretKey "sfsdfsdf"---- | 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- [ testCase "MAP preserves deep stack modifications (#123)" $- contractProp @() @([Integer], Integer) contract- (validateStorageIs @([Integer], Integer) ([257, 43], 2))- dummyContractEnv () ([], 0)- ]--------------------------------------------------------------------------------validateBasic1- :: MonadTest m- => [Integer] -> ContractPropValidator ('TList 'TInt) (m ())-validateBasic1 input (Right (ops, res), _) = do- fromVal res === [sum input + 12, 100]-- annotate "returned no ops"- assert $ null ops-validateBasic1 _ (Left e, _) = failedTest $ show e
− test/Test/Interpreter/Apply.hs
@@ -1,39 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for the 'APPLY' instruction.--module Test.Interpreter.Apply- ( unit_Basic- , unit_Partially_applied_lambda_packed- ) where--import Test.HUnit (Assertion)-import Text.Hex (decodeHex)--import Michelson.Test-import Michelson.Typed--import Test.Util.Contracts--unit_Basic :: Assertion-unit_Basic = do- (_, applyContract) <- importContract (inContractsDir "apply.tz")- let- lam :: Instr '[ ToT (Integer, Integer) ] '[ ToT Integer ]- lam = DUP `Seq` CAR `Seq` DIP CDR `Seq` SUB-- contractPropVal- applyContract (validateStorageIs @Integer 2)- dummyContractEnv (VLam $ RfNormal lam) (VInt 0)--unit_Partially_applied_lambda_packed :: Assertion-unit_Partially_applied_lambda_packed = do- (_, partApplyContract) <-- importContract (inContractsDir "partially-applied-lambda-packed.tz")- let expected = decodeHex "05020000000f0743035b0005034202000000020316"- ?: error "Bad bytes"- contractProp @() @ByteString- partApplyContract (validateStorageIs @ByteString expected)- dummyContractEnv () ""
− test/Test/Interpreter/Auction.hs
@@ -1,142 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test auction.tz contract.------ This spec is an example of using testing capabilities of morley.-module Test.Interpreter.Auction- ( test_Auction- ) where--import Hedgehog (MonadTest, annotate, forAll, property, withTests, (===))-import qualified Hedgehog.Gen as Gen-import Test.Hspec.Expectations (shouldSatisfy)-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.Interpret (ContractEnv(..))-import Michelson.Test- (ContractPropValidator, contractProp, midTimestamp, testTreesWithTypedContract)-import Michelson.Test.Dummy-import Michelson.Test.Gen (genMutez, genTimestamp)-import Michelson.Test.Util (eitherIsLeft, failedTest, runGen)-import Michelson.Typed (Operation'(..), ToT, TransferTokens(..))-import qualified Michelson.Typed as T-import Tezos.Address (Address(..))-import Tezos.Core (Mutez, Timestamp, timestampPlusSeconds, unMutez, unsafeMkMutez, unsafeSubMutez)-import Tezos.Crypto (KeyHash, genKeyHash)--import Test.Util.Contracts--type Storage = (Timestamp, (Mutez, KeyHash))-type Param = KeyHash---- | Spec to test auction.tz contract.------ This test serves as an example on how to test contract with both unit tests--- and Hedgehog.-test_Auction :: IO [TestTree]-test_Auction =- testTreesWithTypedContract (inContractsDir "tezos_examples/attic/auction.tz") auctionTest- where- -- Test auction.tz, everything should be fine- auctionTest contract =- pure- [ testCase "Bid after end of auction triggers failure" $- contractProp contract- (flip shouldSatisfy (isLeft . fst))- (env { ceAmount = unsafeMkMutez 1200 })- keyHash2- (aBitBeforeMidTimestamp, (unsafeMkMutez 1000, keyHash1))-- , testProperty "Random check (sparse distribution)" $ withTests 200 $- hhProp contract genTimestamp genMutez-- , testProperty "Random check (dense end of auction)" $- hhProp contract denseTime genMutez-- , testProperty "Random check (dense amount)" $- hhProp contract genTimestamp denseAmount- ]-- hhProp contract eoaGen amountGen =- property $ do- eoa <- forAll eoaGen- amount <- forAll amountGen- keyHash <- forAll genKeyHash- param <- forAll genKeyHash-- let storage = (eoa, (amount, keyHash))- let validate = validateAuction env param storage- contractProp contract validate env param storage-- aBitBeforeMidTimestamp = midTimestamp `timestampPlusSeconds` -1- -- ^ 1s before NOW-- denseTime = timestampPlusSeconds midTimestamp <$> Gen.enum -4 4- denseAmount = unsafeMkMutez . (midAmount +) . fromInteger <$> Gen.enum -4 4-- env = dummyContractEnv- { ceNow = midTimestamp- , ceAmount = unsafeMkMutez midAmount- }- midAmount = unMutez (maxBound `unsafeSubMutez` minBound) `div` 2--keyHash1 :: KeyHash-keyHash1 = runGen 20 300406 genKeyHash--keyHash2 :: KeyHash-keyHash2 = runGen 20 142917 genKeyHash---- | This validator checks the result of auction.tz execution.------ It checks following properties:------ * Current timestamp is before end of auction--- * Amount of new bid is higher than previous one------ In case of successful execution:------ * End of auction timestamp in updated storage is unchanged--- * Amount in updated storage is equal to @AMOUNT@ of transaction--- * Key hash in updated storage is equal to contract's parameter--- * Script returned exactly one operation, @TransferTokens@, which--- returns money back to the previous bidder-validateAuction- :: MonadTest m- => ContractEnv- -> Param- -> Storage- -> ContractPropValidator (ToT Storage) (m ())-validateAuction env newKeyHash (endOfAuction, (amount, keyHash)) (resE, _)- | ceNow env > endOfAuction- = annotate "Failure didn't trigger on end of auction" >> eitherIsLeft resE- | ceAmount env <= amount- = annotate ("Failure didn't trigger on attempt to bid"- <> " with amount <= than previous bid") >> eitherIsLeft resE- | Left e <- resE- = failedTest $ "Unexpected script fail: " <> show e-- | Right (_, (T.VPair ( T.VTimestamp endOfAuction', _))) <- resE- , endOfAuction /= endOfAuction'- = failedTest "End of auction timestamp of contract changed"-- | Right (_, (T.VPair (_, T.VPair (T.VMutez amount', _)))) <- resE- , amount' /= ceAmount env- = failedTest $ "Storage updated to wrong value: new amount"- <> " is not equal to amount of transaction"- | Right (_, (T.VPair (_, T.VPair (_, T.VKeyHash keyHash')))) <- resE- , keyHash' /= newKeyHash- = failedTest $ "Storage updated to wrong value: new key hash"- <> " is not equal to contract's parameter"-- | Right (ops, _) <- resE- = let annotate' msg =- annotate $ "Invalid money back operation (" <> msg <> ")"- in case ops of- [OpTransferTokens (TransferTokens T.VUnit retAmount (T.VContract retAddr _))] -> do- annotate' "wrong amount" >> retAmount === amount- annotate' "wrong address" >> KeyAddress keyHash === retAddr- _ -> failedTest $ "Unexpected operation list: " <> show ops
− test/Test/Interpreter/Balance.hs
@@ -1,107 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Interpreter.Balance- ( test_balanceIncludesAmount- , test_balanceIncludesAmountComplexCase- ) where--import Hedgehog (Gen, forAll, property, withTests)-import qualified Hedgehog.Gen as Gen-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--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)--genFixture :: Gen Fixture-genFixture = do- fStartingBalance <- unsafeMkMutez <$> Gen.enum 1000 5000- fAmount <- unsafeMkMutez <$> Gen.enum 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" $ withTests 50 $ property $ do- fixture <- forAll genFixture- integrationalTestProp $ scenario checker fixture- ]- 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- 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.- expectStorageConst addressA- $ untypeValue- $ toVal [unsafeMkMutez 75, unsafeMkMutez 100]
− test/Test/Interpreter/CallSelf.hs
@@ -1,111 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for the contract that calls self several times.--module Test.Interpreter.CallSelf- ( test_self_caller- ) where--import Hedgehog (Gen, forAll, property, withTests)-import qualified Hedgehog.Gen as Gen-import Test.HUnit (Assertion, (@?=))-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.Interpret (ContractEnv(..), InterpreterState(..), RemainingSteps(..))-import Michelson.Runtime.GState-import Michelson.Test (ContractPropValidator, contractProp, testTreesWithContract)-import Michelson.Test.Dummy-import Michelson.Test.Integrational-import Michelson.Typed-import qualified Michelson.Untyped as U-import Tezos.Core (unsafeMkMutez)--import Test.Util.Contracts--test_self_caller :: IO [TestTree]-test_self_caller =- testTreesWithContract (inContractsDir "call_self_several_times.tz") $ \selfCaller ->- pure (testImpl selfCaller)--data Fixture = Fixture- { fMaxSteps :: RemainingSteps- , fParameter :: Word64- } deriving stock (Show)--gasForOneExecution :: Num a => a-gasForOneExecution = 19--gasForLastExecution :: Num a => a-gasForLastExecution = 20--fExpectSuccess :: Fixture -> Bool-fExpectSuccess Fixture {..} =- -- note: fParameter must be ≥ 1- fMaxSteps >= fromIntegral ((fParameter - 1) * gasForOneExecution + gasForLastExecution)--genFixture :: Gen Fixture-genFixture =- Fixture <$> (RemainingSteps <$> Gen.enum minGas maxGas) <*> Gen.enum minCalls maxCalls- where- minCalls = 1- maxCalls = 10- minGas = 0- maxGas = gasForOneExecution * maxCalls--type Parameter = 'TInt-type Storage = 'TNat--testImpl ::- (U.Contract, Contract Parameter Storage)- -> [TestTree]-testImpl (uSelfCaller, selfCaller) =- [ testCase ("With parameter 1 single execution consumes " <>- show @_ @Int gasForLastExecution <> " gas") $- contractProp selfCaller (unitValidator gasForLastExecution) unitContractEnv- (1 :: Integer) (0 :: Natural)-- , testCase ("With parameter 2 single execution consumes " <>- show @_ @Int gasForOneExecution <> " gas") $- contractProp selfCaller (unitValidator gasForOneExecution) unitContractEnv- (2 :: Integer) (0 :: Natural)-- , testProperty propertyDescription $- withTests 10 $ property $ do- fixture <- forAll genFixture- integrationalTestProp (integrationalScenario uSelfCaller fixture)- ]- where- -- Environment for unit test- unitContractEnv = dummyContractEnv- -- Validator for unit test- unitValidator ::- RemainingSteps -> ContractPropValidator Storage Assertion- unitValidator gasDiff (_, isRemainingSteps -> remSteps) =- remSteps @?= ceMaxSteps unitContractEnv - gasDiff-- propertyDescription =- "calls itself n times, sets storage to n OR fails due to gas limit"--integrationalScenario :: U.Contract -> Fixture -> IntegrationalScenario-integrationalScenario uSelfCaller fixture = do- setMaxSteps (fMaxSteps fixture)- address <- originate uSelfCaller "self-caller" (U.ValueInt 0) (unsafeMkMutez 1)- let- txData :: TxData- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = U.ValueInt (fromIntegral $ fParameter fixture)- , tdEntrypoint = DefEpName- , tdAmount = minBound- }- if fExpectSuccess fixture- then do- transfer txData address- let expectedStorage = U.ValueInt (fromIntegral $ fParameter fixture)- expectStorageUpdateConst address expectedStorage- else- transfer txData address `catchExpectedError` expectGasExhaustion
− test/Test/Interpreter/ComparableSet.hs
@@ -1,67 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test compare.tz contract.-module Test.Interpreter.ComparableSet- ( test_comparable_set- ) where--import Data.Set as Set (fromList, toList)-import Hedgehog (MonadTest, forAll, property, withTests, (===))-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)--import Michelson.Interpret (InterpreterState, MichelsonFailed)-import Michelson.Test (contractProp, testTreesWithTypedContract)-import Michelson.Test.Dummy-import Michelson.Test.Util (failedTest, genTuple2)-import Michelson.Typed (ToT, fromVal)-import qualified Michelson.Typed as T--import Test.Util.Contracts--type Param = Set (Integer, Integer)-type HContractStorage = Maybe (Integer, Integer)-type ContractStorage = T.Value (ToT HContractStorage)-type ContractResult x- = ( Either MichelsonFailed ([x], ContractStorage)- , InterpreterState)---- | Spec to test comparable_set.tz contract.-test_comparable_set :: IO [TestTree]-test_comparable_set =- testTreesWithTypedContract (inContractsDir "comparable_set.tz") $ \contract ->- let- contractProp' inputParam =- contractProp contract (validate (mkExpected inputParam))- dummyContractEnv inputParam initStorage- in pure- [ testProperty "success test" $ property $- contractProp'- (fromList [ (10, 11) , (10, 12)])- , testProperty "Random check" $- withTests 200 $ property $ do- let genInteger = Gen.integral (Range.linearFrom 0 -1000 1000)- inputParam <- forAll $ Gen.set (Range.linear 0 100) (genTuple2 genInteger genInteger)- contractProp' inputParam- ]- where- initStorage :: HContractStorage- initStorage = Nothing-- mkExpected :: Param -> HContractStorage- mkExpected x = case Set.toList x of- [] -> Nothing- _ -> Just $ maximum x-- validate- :: MonadTest m- => HContractStorage- -> ContractResult x- -> m ()- validate e (Right ([], fromVal -> l), _) = l === e- validate _ (Left _, _) = failedTest "Unexpected fail of script."- validate _ _ = failedTest "Invalid result got."
− test/Test/Interpreter/Compare.hs
@@ -1,61 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test compare.tz contract.-module Test.Interpreter.Compare- ( test_compare- ) where--import Hedgehog (MonadTest, forAll, property, withTests, (===))-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)--import Michelson.Interpret (InterpreterState, MichelsonFailed)-import Michelson.Test (contractProp, testTreesWithTypedContract)-import Michelson.Test.Dummy-import Michelson.Test.Gen (genMutez)-import Michelson.Test.Util (failedTest, genTuple2)-import Michelson.Typed (ToT, fromVal)-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- = ( Either MichelsonFailed ([x], ContractStorage)- , InterpreterState)---- | Spec to test compare.tz contract.-test_compare :: IO [TestTree]-test_compare =- testTreesWithTypedContract (inContractsDir "tezos_examples/macros/compare.tz") $ \contract ->- let- contractProp' inputParam =- contractProp contract (validate (mkExpected inputParam))- dummyContractEnv inputParam initStorage- in pure- [ testProperty "success test" $ property $- contractProp' (unsafeMkMutez 10, unsafeMkMutez 11)- , testProperty "Random check" $- withTests 200 $ property $ do- inputParam <- forAll $ genTuple2 genMutez genMutez- contractProp' inputParam- ]- where- initStorage :: [Bool]- initStorage = []-- mkExpected :: Param -> [Bool]- mkExpected (a, b) = [a == b, a > b, a < b, a >= b, a <= b]-- validate- :: MonadTest m- => [Bool]- -> ContractResult x- -> m ()- validate e (Right ([], fromVal -> l), _) = l === e- validate _ (Left _, _) = failedTest "Unexpected fail of sctipt."- validate _ _ = failedTest "Invalid result got."
− test/Test/Interpreter/ComparePairs.hs
@@ -1,64 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test compare.tz contract.-module Test.Interpreter.ComparePairs- ( test_compare_pairs- ) where--import Hedgehog (MonadTest, forAll, property, withTests, (===))-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)--import Michelson.Interpret (InterpreterState, MichelsonFailed)-import Michelson.Test (contractProp, testTreesWithTypedContract)-import Michelson.Test.Dummy-import Michelson.Test.Gen (genMutez)-import Michelson.Test.Util (failedTest, genTuple2)-import Michelson.Typed (ToT, fromVal)-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- = ( Either MichelsonFailed ([x], ContractStorage)- , InterpreterState)---- | Spec to test compare.tz contract.-test_compare_pairs :: IO [TestTree]-test_compare_pairs =- testTreesWithTypedContract (inContractsDir "compare_pairs.tz") $ \contract ->- let- contractProp' inputParam =- contractProp contract (validate (mkExpected inputParam))- dummyContractEnv inputParam initStorage- in pure- [ testProperty "success test" $ property $- contractProp'- ( (unsafeMkMutez 10, unsafeMkMutez 11)- , (unsafeMkMutez 10, unsafeMkMutez 12)- )- , testProperty "Random check" $- withTests 200 $ property $ do- inputParam <- forAll $ genTuple2 (genTuple2 genMutez genMutez) (genTuple2 genMutez genMutez)- contractProp' inputParam- ]- where- initStorage :: [Bool]- initStorage = []-- mkExpected :: Param -> [Bool]- mkExpected (a, b) = [a == b, a > b, a < b, a >= b, a <= b]-- validate- :: MonadTest m- => [Bool]- -> ContractResult x- -> m ()- validate e (Right ([], fromVal -> l), _) = l === e- validate _ (Left _, _) = failedTest "Unexpected fail of sctipt."- validate _ _ = failedTest "Invalid result got."
− test/Test/Interpreter/Conditionals.hs
@@ -1,62 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test conditionals.tz contract.-module Test.Interpreter.Conditionals- ( test_conditionals- ) where--import Hedgehog (MonadTest, forAll, property, withTests, (===))-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)--import Michelson.Interpret (InterpreterState, MichelsonFailed)-import Michelson.Test (contractProp, testTreesWithTypedContract)-import Michelson.Test.Dummy (dummyContractEnv)-import Michelson.Test.Util (eitherIsLeft, eitherIsRight, failedTest, genEither)-import Michelson.Text-import qualified Michelson.Typed as T--import Test.Util.Contracts--type Param = Either MText (Maybe Integer)-type ContractStorage = T.Value (T.ToT MText)-type ContractResult x- = ( Either MichelsonFailed ([x], ContractStorage)- , InterpreterState)---- | Spec to test conditionals.tz contract.-test_conditionals :: IO [TestTree]-test_conditionals =- testTreesWithTypedContract (inContractsDir "tezos_examples/attic/conditionals.tz") $ \contract ->- let- contractProp' :: MonadTest m => Param -> m ()- contractProp' inputParam =- contractProp contract (validate inputParam) dummyContractEnv inputParam- [mt|storage|]- in pure- [ testProperty "success 1 test" $- property $ contractProp' $ Left [mt|abc|]- , testProperty "Random check" $- withTests 200 $ property $ do- inputParam <- forAll $ genEither- genMText- (Gen.maybe (Gen.integral (Range.linearFrom 0 -1000 1000)))- contractProp' inputParam- ]- where- validate- :: MonadTest m- => Show x- => Param- -> ContractResult x- -> m ()- validate (Left a) (Right ([], T.VString b), _) = a === b- validate (Right Nothing) r = eitherIsLeft $ fst r- validate (Right (Just a)) r- | a < 0 = eitherIsLeft $ fst r- | otherwise = eitherIsRight $ fst r- validate _ res = failedTest $ "Unexpected result: " <> show res
− test/Test/Interpreter/ContractOp.hs
@@ -1,77 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Module, containing spec to test contract_op.tz contract.-module Test.Interpreter.ContractOp- ( test_contract_op- ) where--import qualified Data.Map as M-import Fmt (pretty)-import Hedgehog (MonadTest, property, (===))-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)--import Michelson.Interpret (ContractEnv(..), ContractReturn)-import Michelson.Test (contractProp, dummyContractEnv, failedTest, testTreesWithTypedContract)-import Michelson.Typed (Contract, ToT, fromVal)-import Michelson.Untyped (ParameterType(..), T(..), Type(..), noAnn)-import Tezos.Address--import Test.Util.Contracts---- | Spec to test @contract_op.tz@ contract.------ Test results are confirmed by the reference implementation.-test_contract_op :: IO [TestTree]-test_contract_op =- testTreesWithTypedContract (inContractsDir "contract_op.tz") $ \contract -> pure $- [ testProperty "contract not found" $ property $- contractProp' False [] contract- ] <>- map (\(res, paramType) ->- testProperty (msg res paramType) $ property $- contractProp' res [(addr, paramType)] contract- )- [ (True, ParameterType intQ "root")- , (True, ParameterType int "root")- , (False, ParameterType intQ noAnn)- , (False, ParameterType int noAnn)- , (False, ParameterType intP noAnn)- , (False, ParameterType string noAnn)- , (False, ParameterType intP "root")- , (False, ParameterType intQ "another_root")- ]- where- msg isGood paramType =- "parameter in environment is '" <> pretty paramType <> "', " <>- bool "" "but " isGood <> "contract expects '%root int :q'"-- intQ = Type TInt "q"- int = Type TInt noAnn- intP = Type TInt "p"- string = Type TString noAnn-- addr = unsafeParseContractHash "KT1WsLzQ61xtMNJHfwgCHh2RnALGgFAzeSx9"-- validate- :: MonadTest m- => Bool- -> ContractReturn (ToT Bool)- -> m ()- validate ex (Right ([], fromVal -> l), _) = l === ex- validate _ (Left _, _) = failedTest "Unexpected fail in interepreter"- validate _ _ = failedTest "Unexpected result of script execution"-- contractProp'- :: MonadTest m- => Bool -> [(ContractHash, ParameterType)] -> Contract (ToT Address) (ToT Bool)- -> m ()- contractProp' res ctrs contract =- contractProp- contract- (validate res)- dummyContractEnv {ceContracts = M.fromList ctrs}- (ContractAddress addr)- False
− test/Test/Interpreter/EnvironmentSpec.hs
@@ -1,122 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for the 'environment.tz' contract--module Test.Interpreter.EnvironmentSpec- ( test_environment- ) where--import Hedgehog (Gen, forAll, property, withTests)-import qualified Hedgehog.Gen as Gen-import Test.Tasty (TestTree)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.Interpret (RemainingSteps(..))-import Michelson.Runtime.GState-import Michelson.Test (testTreesWithContract)-import Michelson.Test.Integrational-import Michelson.Typed-import qualified Michelson.Typed as T-import qualified Michelson.Untyped as U-import Tezos.Address-import Tezos.Core-import Tezos.Crypto (detSecretKey, toPublic)--import Test.Util.Contracts--test_environment :: IO [TestTree]-test_environment =- testTreesWithContract (inContractsDir "environment.tz") $- \contract -> pure- [ testImpl contract- , testCase ("Default balance") $- integrationalTestExpectation testDefaultBalance- ]--data Fixture = Fixture- { fNow :: Timestamp- , fMaxSteps :: RemainingSteps- , fPassOriginatedAddress :: Bool- , fBalance :: Mutez- , fAmount :: Mutez- } deriving stock (Show)--genFixture :: Gen Fixture-genFixture = do- fNow <- timestampFromSeconds <$> Gen.enum 100000 111111- fMaxSteps <- RemainingSteps <$> Gen.enum 1015 1028- fPassOriginatedAddress <- Gen.bool- fBalance <- unsafeMkMutez <$> Gen.enum 1 1234- fAmount <- unsafeMkMutez <$> Gen.enum 1 42- return Fixture {..}--shouldExpectFailed :: Fixture -> Bool-shouldExpectFailed fixture =- or- [ fBalance fixture `unsafeAddMutez` fAmount fixture > unsafeMkMutez 1000- , fNow fixture < timestampFromSeconds 100500- , fPassOriginatedAddress fixture- , fAmount fixture < unsafeMkMutez 15- ]--testImpl- :: (U.Contract, T.Contract 'TAddress 'TUnit)- -> TestTree-testImpl (uEnvironment, _environment) = do- testProperty description $- withTests 50 $ property $ do- fixture <- forAll genFixture- integrationalTestProp (scenario fixture)- where- scenario :: Fixture -> IntegrationalScenario- scenario = integrationalScenario uEnvironment- description =- -- The conditions under which this contract fails are described in a comment- -- at the beginning of the contract.- "contract fails under certain conditions and returns whether remaining gas > 1000"--integrationalScenario :: U.Contract -> Fixture -> IntegrationalScenario-integrationalScenario contract fixture = do- -- First of all let's set desired gas limit and NOW- setNow $ fNow fixture- setMaxSteps $ fMaxSteps fixture-- -- Then let's originated the 'environment.tz' contract- environmentAddress <-- originate contract "environment" U.ValueUnit (fBalance fixture)-- -- And transfer tokens to it- let- param- | fPassOriginatedAddress fixture = environmentAddress- | otherwise = genesisAddress- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = U.ValueString (mformatAddress param)- , tdEntrypoint = DefEpName- , tdAmount = fAmount fixture- }- transferToEnvironment = transfer txData environmentAddress-- -- Execute operations and check that interpreter fails when one of- -- failure conditions is met or updates environment's storage- -- approriately- if shouldExpectFailed fixture- then transferToEnvironment `catchExpectedError` expectMichelsonFailed (const True) environmentAddress- else transferToEnvironment--testDefaultBalance :: IntegrationalScenario-testDefaultBalance = do- let genKey seed = mkKeyAddress . toPublic . detSecretKey $ seed- addr = genKey "random address testDefaultBalance"- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = U.ValueString mempty- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 1- }- transfer txData addr- expectBalance addr (unsafeMkMutez 1)
− test/Test/Interpreter/StackRef.hs
@@ -1,37 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Interpreter tests involving 'StackRef'.--module Test.Interpreter.StackRef- ( test_mkStackRef- ) where--import Test.Hspec.Expectations (shouldSatisfy)-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)--import Michelson.Test (contractProp)-import Michelson.Test.Dummy (dummyContractEnv)-import Michelson.Typed--test_mkStackRef :: TestTree-test_mkStackRef =- testCase "does not segfault" $- contractProp contract (flip shouldSatisfy isRight . fst)- dummyContractEnv () ()- where- stackRef = PrintComment . one . Right $ mkStackRef @1-- contract :: Contract 'TUnit 'TUnit- contract = Contract- { cCode = contractCode- , cStoreNotes = starNotes- , cParamNotes = starParamNotes- }-- contractCode :: ContractCode 'TUnit 'TUnit- contractCode =- CAR `Seq` DUP `Seq` Ext (PRINT stackRef) `Seq`- DROP `Seq` NIL `Seq` PAIR
− test/Test/Interpreter/StringCaller.hs
@@ -1,140 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for the 'stringCaller.tz' contract and its interaction with--- the 'failOrStoreAndTransfer.tz' contract. Both of them have comments describing--- their behavior.--module Test.Interpreter.StringCaller- ( test_stringCaller- ) where--import Hedgehog (forAll, property, withTests)-import Test.Tasty (testGroup, TestTree)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.Runtime.GState-import Michelson.Test (testTreesWithContract)-import Michelson.Test.Integrational-import Michelson.Text-import Michelson.Typed-import qualified Michelson.Typed as T-import qualified Michelson.Untyped as U-import Tezos.Address-import Tezos.Core--import Test.Util.Contracts--test_stringCaller :: IO [TestTree]-test_stringCaller =- testTreesWithContract (inContractsDir "string_caller.tz") $ \stringCaller ->- testTreesWithContract (inContractsDir "fail_or_store_and_transfer.tz") $- \failOrStoreAndTransfer ->- pure $ [testImpl stringCaller failOrStoreAndTransfer]--testImpl ::- (U.Contract, T.Contract 'TString 'TAddress)- -> (U.Contract, T.Contract 'TString 'TString)- -> TestTree-testImpl (uStringCaller, _stringCaller) (uFailOrStore, _failOrStoreAndTransfer) =- let scenario = integrationalScenario uStringCaller uFailOrStore- in- -- failOrStoreAndTransfer's pre-conditions:- -- balance is >= 1300 and NOW >= 500- testGroup "calls failOrStoreAndTransfer, updates storage & balance, checks pre-conditions"- [ testCase "when parameter is a constant" $- integrationalTestExpectation (scenario constStr)- -- The test is trivial, so it's kinda useless to run it many times- , testProperty "when parameter is an arbitrary value" $- withTests 2 $ property $ do- mtext <- forAll genMText- integrationalTestProp (scenario mtext)- ]- where- constStr = [mt|caller|]--integrationalScenario :: U.Contract -> U.Contract -> MText -> IntegrationalScenario-integrationalScenario stringCaller failOrStoreAndTransfer str = do- let- initFailOrStoreBalance = unsafeMkMutez 900- initStringCallerBalance = unsafeMkMutez 500-- -- Originate both contracts- failOrStoreAndTransferAddress <-- originate failOrStoreAndTransfer "failOrStoreAndTransfer" (U.ValueString [mt|hello|]) initFailOrStoreBalance- stringCallerAddress <-- originate stringCaller "stringCaller"- (U.ValueString $ mformatAddress failOrStoreAndTransferAddress)- initStringCallerBalance-- -- NOW = 500, so stringCaller shouldn't fail- setNow (timestampFromSeconds 500)-- -- Transfer 100 tokens to stringCaller, it should transfer 300 tokens- -- to failOrStoreAndTransfer- let- newValue = untypeValue $ toVal str- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = newValue- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 100- }- transferToStringCaller = transfer txData stringCallerAddress- transferToStringCaller-- -- Execute operations and check balances and storage of 'failOrStoreAndTransfer'- do- let- -- `stringCaller.tz` transfers 300 mutez.- -- 'failOrStoreAndTransfer.tz' transfers 5 tokens.- -- Also 100 tokens are transferred from the genesis address.- expectedStringCallerBalance = unsafeMkMutez (500 - 300 + 100)- expectedFailOrStoreBalance = unsafeMkMutez (900 + 300 - 5)- expectedConstAddrBalance = unsafeMkMutez 5-- expectStorageUpdateConst failOrStoreAndTransferAddress newValue- expectBalance failOrStoreAndTransferAddress expectedFailOrStoreBalance- expectBalance stringCallerAddress expectedStringCallerBalance- expectBalance constAddr expectedConstAddrBalance-- -- Now let's transfer 100 tokens to stringCaller again.- -- This time execution should fail, because failOrStoreAndTransfer should fail- -- because its balance is greater than 1300.- transferToStringCaller `catchExpectedError`- expectMichelsonFailed (const True) failOrStoreAndTransferAddress-- -- We can also send tokens from failOrStoreAndTransfer to tz1 address directly- let- txDataToConst = TxData- { tdSenderAddress = failOrStoreAndTransferAddress- , tdParameter = U.ValueUnit- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 200- }- transfer txDataToConst constAddr-- -- Let's check balance of failOrStoreAndTransfer and tz1 address.- -- We transferred 200 tokens from failOrStoreAndTransferAddress to constAddr.- do- let- expectedFailOrStoreBalance = unsafeMkMutez (900 + 300 - 5 - 200)- expectedConstAddrBalance = unsafeMkMutez (5 + 200)-- expectBalance failOrStoreAndTransferAddress expectedFailOrStoreBalance- expectBalance constAddr expectedConstAddrBalance-- -- Now we can transfer to stringCaller again and it should succeed- -- this time, because the balance of failOrStoreAndTransfer decreased- transferToStringCaller-- -- Now let's set NOW to 600 and expect stringCaller to fail- setNow (timestampFromSeconds 600)- transferToStringCaller `catchExpectedError`- expectMichelsonFailed (const True) stringCallerAddress---- Address hardcoded in 'failOrStoreAndTransfer.tz'.-constAddr :: Address-constAddr = unsafeParseAddress "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU"
− test/Test/Macro.hs
@@ -1,193 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Macro- ( unit_PAPAIR- , unit_UNPAIR- , unit_CADR- , unit_SET_CADR- , unit_MAP_CADR- , unit_mapLeaves- , unit_expand- , unit_expandValue- ) where--import Test.Hspec (Expectation, shouldBe)--import Michelson.ErrorPos (InstrCallStack(..), LetName(..), SrcPos, srcPos)-import Michelson.Macro-import Michelson.Untyped (ExpandedOp(..), InstrAbstract(..), Value, Value'(..), ann, noAnn)--defPos :: SrcPos-defPos = srcPos 1 1--defICS :: InstrCallStack-defICS = InstrCallStack [] defPos---- TODO: it seems to me that these duplicated "where" blocks should be--- replaced with some reasonable mini-EDSL - at least to facilitate tests--- writing - and that would be a rather big refactoring.--- Dunno how to deal with this duplication otherwise.-{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-}--unit_PAPAIR :: Expectation-unit_PAPAIR = do- expandPapair defICS pair n n `shouldBe` [primEx $ PAIR n n n n]- expandPapair defICS (P leaf pair) n n `shouldBe`- [primEx $ DIP (expandMacro defICS $ PAPAIR pair n n), primEx $ PAIR n n n n]- expandList [mac $ PAPAIR (P pair leaf) n n] `shouldBe`- [WithSrcEx defICS $ SeqEx [primEx $ PAIR n n n n, primEx $ PAIR n n n n]]- expandList [mac $ PAPAIR (P pair pair) n n] `shouldBe`- [WithSrcEx defICS $ SeqEx [primEx (PAIR n n n n),- primEx (DIP [primEx (PAIR n n n n)]),- primEx (PAIR n n n n)]]- where- mac = flip Mac defPos- primEx = PrimEx- n = noAnn- leaf = F (n, n)- pair = P leaf leaf--unit_UNPAIR :: Expectation-unit_UNPAIR = do- expandUnpapair defICS pair `shouldBe`- [primEx $ DUP n, primEx $ CAR n n, primEx $ DIP [primEx $ CDR n n]]- expandList [mac $ UNPAIR $ P leaf pair] `shouldBe`- [WithSrcEx defICS $ SeqEx [primEx (DUP n),- primEx (CAR n n),- primEx (DIP [primEx (CDR n n),- primEx (DUP n),- primEx (CAR n n),- primEx (DIP [primEx (CDR n n)])])]]- expandList [mac $ UNPAIR $ P pair leaf] `shouldBe`- [WithSrcEx defICS $ SeqEx [primEx (DUP n),- primEx (DIP [primEx (CDR n n)]),- primEx (CAR n n),- primEx (DUP n),- primEx (CAR n n),- primEx (DIP [primEx (CDR n n)])]]- expandList [mac $ UNPAIR $ P pair pair] `shouldBe`- [WithSrcEx defICS $ SeqEx $ expandP ++ [primEx $ DIP expandP] ++ expandP]- where- mac = flip Mac defPos- primEx = PrimEx- expandP = primEx <$> [DUP n, CAR n n, DIP [primEx $ CDR n n]]- n = noAnn- leaf = F (n, n)- pair = P leaf leaf--unit_CADR :: Expectation-unit_CADR = do- expandCadr defICS ([A]) v f `shouldBe` [primEx $ CAR v f]- expandCadr defICS ([D]) v f `shouldBe` [primEx $ CDR v f]- expandCadr defICS (A:xs) v f `shouldBe` primEx (CAR n n) : expandMacro defICS (CADR xs v f)- expandCadr defICS (D:xs) v f `shouldBe` primEx (CDR n n) : expandMacro defICS (CADR xs v f)- where- primEx = PrimEx- v = ann "var"- f = ann "field"- n = noAnn- xs = [A, D]--unit_SET_CADR :: Expectation-unit_SET_CADR = do- expandSetCadr defICS [A] v f `shouldBe` primEx <$> [ DUP noAnn, CAR noAnn f, DROP- , CDR (ann "%%") noAnn, SWAP, PAIR noAnn v f (ann "@")]- expandSetCadr defICS [D] v f `shouldBe` primEx <$> [ DUP noAnn, CDR noAnn f, DROP- , CAR (ann "%%") noAnn, PAIR noAnn v (ann "@") f]- expandSetCadr defICS (A:xs) v f `shouldBe`- primEx <$> [DUP noAnn, DIP (primEx carN : expandMacro defICS (SET_CADR xs noAnn f)), cdrN, SWAP, pairN]- expandSetCadr defICS (D:xs) v f `shouldBe`- primEx <$> [DUP noAnn, DIP (primEx cdrN : expandMacro defICS (SET_CADR xs noAnn f)), carN, pairN]- where- primEx = PrimEx- v = ann "var"- f = ann "field"- xs = [A, D]- carN = CAR noAnn noAnn- cdrN = CDR noAnn noAnn- pairN = PAIR noAnn v noAnn noAnn--unit_MAP_CADR :: Expectation-unit_MAP_CADR = do- expandMapCadr defICS [A] v f ops `shouldBe`- primEx <$> [DUP noAnn, cdrN, DIP [primEx $ CAR noAnn f, SeqEx ops'], SWAP, pairN]- expandMapCadr defICS [D] v f ops `shouldBe`- concat [primEx <$> [DUP noAnn, CDR noAnn f], [SeqEx ops'], primEx <$> [SWAP, carN, pairN]]- expandMapCadr defICS (A:xs) v f ops `shouldBe`- primEx <$> [DUP noAnn, DIP (primEx carN : expandMacro defICS (MAP_CADR xs noAnn f ops)), cdrN, SWAP, pairN]- expandMapCadr defICS (D:xs) v f ops `shouldBe`- primEx <$> [DUP noAnn, DIP (primEx cdrN : expandMacro defICS (MAP_CADR xs noAnn f ops)), carN, pairN]- where- primEx = PrimEx- v = ann "var"- f = ann "field"- n = noAnn- xs = [A, D]- ops = [Prim (DUP n) defPos]- ops' = [WithSrcEx defICS $ PrimEx (DUP n)]- carN = CAR noAnn noAnn- cdrN = CDR noAnn noAnn- pairN = PAIR noAnn v noAnn noAnn--unit_mapLeaves :: Expectation-unit_mapLeaves = do- mapLeaves [(v, f), (v, f)] pair `shouldBe` P (F (v, f)) (F (v, f))- mapLeaves annotations (P pair (F (n, n))) `shouldBe`- P (P (leaf "var1" "field1") (leaf "var2" "field2")) (leaf "var3" "field3")- mapLeaves annotations (P pair pair) `shouldBe`- P (P (leaf "var1" "field1") (leaf "var2" "field2")) (P (leaf "var3" "field3") (F (n, n)))- where- annotations = zip (ann <$> ["var1", "var2", "var3"]) (ann <$> ["field1", "field2", "field3"])- n = noAnn- v = ann "var"- f = ann "field"- leaf v' f' = F (ann v', ann f')- pair = P (F (n, n)) (F (n, n))--unit_expand :: Expectation-unit_expand = do- expand [LetName "a"] diip `shouldBe` expandedDiip- expand [LetName "a"] (prim $ IF [diip] [diip]) `shouldBe` (primEx $ IF [expandedDiip] [expandedDiip])- expand [LetName "a"] (Seq [diip, diip] defPos) `shouldBe` (WithSrcEx aIcs $ SeqEx $ [expandedDiip, expandedDiip])- where- aIcs = InstrCallStack [LetName "a"] defPos- prim = flip Prim defPos- primEx = WithSrcEx aIcs . PrimEx- mac = flip Mac defPos- diip :: ParsedOp- diip = mac (DIIP 2 [prim SWAP])- expandedDiip :: ExpandedOp- expandedDiip = primEx (DIPN 2 [primEx SWAP])--unit_expandValue :: Expectation-unit_expandValue = do- expandValue parsedPair `shouldBe` expandedPair- expandValue parsedPapair `shouldBe` expandedPapair- expandValue parsedLambdaWithMac `shouldBe` expandedLambdaWithMac- where- mac = flip Mac defPos- primEx = PrimEx-- parsedPair :: Value' ParsedOp- parsedPair = ValuePair (ValueInt 5) (ValueInt 5)-- expandedPair :: Value- expandedPair = ValuePair (ValueInt 5) (ValueInt 5)-- parsedPapair :: Value' ParsedOp- parsedPapair = ValuePair (ValuePair (ValueInt 5) (ValueInt 5)) (ValueInt 5)-- expandedPapair :: Value- expandedPapair = ValuePair (ValuePair (ValueInt 5) (ValueInt 5)) (ValueInt 5)-- parsedLambdaWithMac :: Value' ParsedOp- parsedLambdaWithMac = ValueLambda $- one (mac (PAPAIR (P (F (noAnn, noAnn)) (P (F (noAnn, noAnn)) (F (noAnn, noAnn)))) noAnn noAnn))-- expandedLambdaWithMac :: Value- expandedLambdaWithMac = ValueLambda . one $ WithSrcEx defICS $ SeqEx- [ primEx $ DIP [primEx $ PAIR noAnn noAnn noAnn noAnn]- , primEx $ PAIR noAnn noAnn noAnn noAnn- ]
− test/Test/Michelson/Runtime.hs
@@ -1,229 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for Michelson.Runtime.--module Test.Michelson.Runtime- ( test_executorPure- ) where--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)-import Test.Tasty.HUnit (testCase)--import Michelson.ErrorPos (InstrCallStack(..), Pos(..), SrcPos(..))-import Michelson.Interpret (ContractEnv(..), InterpretResult(..), interpretUntyped)-import Michelson.Runtime hiding (transfer)-import Michelson.Runtime.GState (GState(..), genesisAddress, initGState)-import Michelson.Test.Dummy (dummyContractEnv, dummyMaxSteps, dummyNow, dummyOrigination)-import Michelson.Test.Integrational- (IntegrationalScenario, TestError(CustomTestError), catchExpectedError, integrationalFail,- integrationalTestExpectation, originate, transfer, unexpectedInterpreterError)-import Michelson.Text (mt)-import Michelson.Typed (untypeValue)-import Michelson.Untyped-import Tezos.Address-import Tezos.Core (unsafeMkMutez)--import Test.Util.Contracts (contractsDir)--test_executorPure :: IO [TestTree]-test_executorPure = do- illTypedContract <-- prepareContract (Just (contractsDir </> "ill-typed/sum_strings.tz"))- pure- [ testGroup "Updates storage value of executed contract" $- [ testCase "contract1" $ updatesStorageValue contractAux1- , testCase "contract2" $ updatesStorageValue contractAux2- ]- , testCase "Succeeds to originate the same contract twice, with different addresses"- succeedsToOriginateTwice- , testCase "Fails to originate an ill-typed contract"- (failsToOriginateIllTyped (ValueString [mt||]) illTypedContract)- , testCase "Fails transfering 0tz to plain account"- $ integrationalTestExpectation testZeroTransactionFails- , testCase "Success transfering 0tz to a contract"- $ integrationalTestExpectation testZeroTransactionSuccess- , testCase "Transfer of 0tz from unknown address is allowed" transferFromUnknown- ]--------------------------------------------------------------------------------- Test code--------------------------------------------------------------------------------- | Data type, that containts contract and its auxiliary data.------ This type is mostly used for testing purposes.-data ContractAux = ContractAux- { caContract :: Contract- , caEnv :: ContractEnv- , caStorage :: Value- , caParameter :: Value- }--updatesStorageValue :: ContractAux -> Assertion-updatesStorageValue ca = either (assertFailure . pretty) handleResult $ do- let- ce = caEnv ca- origination = contractAuxToOrigination ca- txData = TxData- { tdSenderAddress = ceSender ce- , tdParameter = caParameter ca- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 100- }-- runExecutorM dummyNow dummyMaxSteps initGState $ do- addr <- withGlobalOperation (OriginateOp origination)- $ executeOrigination origination- executeGlobalOperations [TransferOp addr txData]- return addr- where- toNewStorage :: InterpretResult -> Value- toNewStorage InterpretResult {..} = untypeValue iurNewStorage-- handleResult :: (ExecutorRes, Address) -> Assertion- handleResult (ir, addr) = do- expectedValue <-- either (assertFailure . pretty) (pure . toNewStorage) $- interpretUntyped- (caContract ca) (caParameter ca) (caStorage ca) (caEnv ca)- case gsAddresses (_erGState ir) ^. at addr of- Nothing -> expectationFailure $ "Address not found: " <> pretty addr- Just (ASContract cs) -> csStorage cs @?= expectedValue- Just _ -> expectationFailure $ "Address has unexpected state " <> pretty addr--succeedsToOriginateTwice :: Expectation-succeedsToOriginateTwice = either (assertFailure . pretty) handleResult $ do- runExecutorM dummyNow dummyMaxSteps initGState $ do- addr1 <- withGlobalOperation (OriginateOp origination)- $ executeOrigination origination- addr2 <- withGlobalOperation (OriginateOp origination)- $ executeOrigination origination- return (addr1, addr2)- where- contract = caContract contractAux1- origination = dummyOrigination (caStorage contractAux1) contract-- handleResult :: (ExecutorRes, (Address, Address)) -> Assertion- handleResult (_, (addr1, addr2)) =- addr1 /= addr2 @? "Two originated addresses are not different"--failsToOriginateIllTyped :: Value -> Contract -> Expectation-failsToOriginateIllTyped initialStorage illTypedContract =- simpleTest ops isIllTypedContract- where- origination = dummyOrigination initialStorage illTypedContract- ops = [OriginateOp origination]- isIllTypedContract (Left (EEIllTypedContract {})) = True- isIllTypedContract _ = False--simpleTest- :: HasCallStack- => [ExecutorOp]- -> (Either ExecutorError ExecutorRes -> Bool)- -> Expectation-simpleTest ops predicate =- fst <$> runExecutorM dummyNow dummyMaxSteps initGState (executeGlobalOperations ops)- `shouldSatisfy` predicate--testZeroTransactionFails :: IntegrationalScenario-testZeroTransactionFails = do- let- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = ValueNil- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 0 }-- transfer txData genesisAddress `catchExpectedError`- \case- EEZeroTransaction addr- | addr == genesisAddress -> pass- | otherwise -> integrationalFail $ CustomTestError $- "Expected " +| genesisAddress |+ ", but got " +| addr |+ ""- err -> unexpectedInterpreterError err "expected attempt to send 0tz"--testZeroTransactionSuccess :: IntegrationalScenario-testZeroTransactionSuccess = do- let- contract = caContract contractAux1- storage = caStorage contractAux1- balance = ceBalance . caEnv $ contractAux1- txData = TxData- { tdSenderAddress = genesisAddress- , tdParameter = caParameter contractAux1- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 0 }- address <- originate contract "test0tzContract" storage balance- transfer txData address--transferFromUnknown :: Assertion-transferFromUnknown = do- let- res = runExecutorM dummyNow dummyMaxSteps initGState $ do- addr <- withGlobalOperation (OriginateOp origination)- $ executeOrigination origination- executeGlobalOperations [TransferOp addr txData]-- whenLeft res $- assertFailure . pretty- where- ca = contractAux1- origination = contractAuxToOrigination ca- txData =- TxData- { tdSenderAddress = detGenKeyAddress "transferFromUnknown"- , tdParameter = caParameter ca- , tdEntrypoint = DefEpName- , tdAmount = unsafeMkMutez 0- }--------------------------------------------------------------------------------- Data-------------------------------------------------------------------------------ics :: Word -> InstrCallStack-ics x = InstrCallStack [] (SrcPos (Pos x) (Pos 0))--contractAux1 :: ContractAux-contractAux1 = ContractAux- { caContract = contract- , caEnv = dummyContractEnv- , caStorage = ValueTrue- , caParameter = ValueString [mt|aaa|]- }- where- contract :: Contract- contract = Contract- { contractParameter = ParameterType (Type TString noAnn) noAnn- , contractStorage = Type TBool noAnn- , contractCode =- [ WithSrcEx (ics 0) $ PrimEx (CDR noAnn noAnn)- , WithSrcEx (ics 1) $ PrimEx (NIL noAnn noAnn $ Type TOperation noAnn)- , WithSrcEx (ics 2) $ PrimEx (PAIR noAnn noAnn noAnn noAnn)- ]- }--contractAux2 :: ContractAux-contractAux2 = contractAux1- { caContract = (caContract contractAux1)- { contractCode =- [ WithSrcEx (ics 0) $ PrimEx (CDR noAnn noAnn)- , WithSrcEx (ics 1) $ PrimEx (NOT noAnn)- , WithSrcEx (ics 2) $ PrimEx (NIL noAnn noAnn $ Type TOperation noAnn)- , WithSrcEx (ics 3) $ PrimEx (PAIR noAnn noAnn noAnn noAnn)- ]- }- }--contractAuxToOrigination :: ContractAux -> OriginationOperation-contractAuxToOrigination ca =- let contract = caContract ca- ce = caEnv ca- in (dummyOrigination (caStorage ca) contract) {ooBalance = ceBalance ce}
− test/Test/Michelson/Text.hs
@@ -1,164 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests on 'MText'.-module Test.Michelson.Text- ( test_Roundtrip- , unit_mkMText- , unit_parse- , unit_mkMTextCut- , unit_QuasiQuoter- , unit_mt- ) where--import Test.HUnit (Assertion, (@?), (@?=))-import Test.Tasty (TestTree)-import Fmt (pretty)-import qualified Text.Megaparsec as P--import Michelson.Parser-import qualified Michelson.Untyped as U-import Michelson.Text--import Test.Util.Hedgehog (roundtripTree)---- | Parse string literal content to 'MText'.-parseMTextTest :: Text -> Either () MText-parseMTextTest t =- first (const ()) $- expectString <$> parseNoEnv (stringLiteral <* P.eof) "" ("\"" <> t <> "\"")- where- expectString = \case- U.ValueString t' -> t'- o -> error $ "Expected string, but got " <> pretty o---- | 'MText' rountrip conversions.-test_Roundtrip :: [TestTree]-test_Roundtrip =- [ roundtripTree genMText writeMText parseMTextTest ]---- | Check value against the given predicate.-(@??) :: (Show a, HasCallStack) => a -> (a -> Bool) -> Assertion-(@??) val predicate =- predicate val @?- ("Predicate does not hold for value " <> show val)--unit_mkMText :: Assertion-unit_mkMText = do- mkMText ""- @?= Right (MTextUnsafe "")- mkMText "a ba"- @?= Right (MTextUnsafe "a ba")- mkMText "a\nb"- @?= Right (MTextUnsafe "a\nb")- mkMText "a\\nb"- @?= Right (MTextUnsafe "a\\nb")- mkMText "\\\\"- @?= Right (MTextUnsafe "\\\\")- mkMText "\""- @?= Right (MTextUnsafe "\"")- mkMText "\r"- @?? isLeft- mkMText "\t"- @?? isLeft- mkMText (toText @String [toEnum 5])- @?? isLeft- mkMText (toText @String [toEnum 127])- @?? isLeft- mkMText (toText @String [toEnum 300])- @?? isLeft--unit_parse :: Assertion-unit_parse = do- parseMTextTest ""- @?= Right (MTextUnsafe "")- parseMTextTest "a ba"- @?= Right (MTextUnsafe "a ba")- parseMTextTest "a\nb"- @?? isLeft- parseMTextTest "a\\nb"- @?= Right (MTextUnsafe "a\nb")- parseMTextTest "\\"- @?? isLeft- parseMTextTest "\\\\"- @?= Right (MTextUnsafe "\\")- parseMTextTest "\""- @?? isLeft- parseMTextTest "\\\""- @?= Right (MTextUnsafe "\"")- parseMTextTest "\r"- @?? isLeft- parseMTextTest "\t"- @?? isLeft- parseMTextTest (toText @String [toEnum 5])- @?? isLeft- parseMTextTest (toText @String [toEnum 127])- @?? isLeft- parseMTextTest (toText @String [toEnum 300])- @?? isLeft--unit_mkMTextCut :: Assertion-unit_mkMTextCut = do- mkMTextCut ""- @?= MTextUnsafe ""- mkMTextCut "a ba"- @?= MTextUnsafe "a ba"- mkMTextCut "a\nb"- @?= MTextUnsafe "a\nb"- mkMTextCut "a\\nb"- @?= MTextUnsafe "a\\nb"- mkMTextCut "\\"- @?= MTextUnsafe "\\"- mkMTextCut "\\\\"- @?= MTextUnsafe "\\\\"- mkMTextCut "\""- @?= MTextUnsafe "\""- mkMTextCut "\\\""- @?= MTextUnsafe "\\\""- mkMTextCut "a\rb"- @?= MTextUnsafe "ab"- mkMTextCut "c\td\r"- @?= MTextUnsafe "cd"- mkMTextCut (toText @String [toEnum 5])- @?= MTextUnsafe ""- mkMTextCut (toText @String [toEnum 127])- @?= MTextUnsafe ""- mkMTextCut (toText @String [toEnum 300, 'A'])- @?= MTextUnsafe "A"--unit_QuasiQuoter :: Assertion-unit_QuasiQuoter = do- qqMText ""- @?= Right ""- qqMText "a ba"- @?= Right "a ba"- qqMText "a\nb"- @?? isLeft- qqMText "a\\nb"- @?= Right "a\nb"- qqMText "\\"- @?? isLeft- qqMText "\\\\"- @?= Right "\\"- qqMText "\""- @?= Right "\""- qqMText "\\\""- @?? isLeft- qqMText "\r"- @?? isLeft- qqMText "\t"- @?? isLeft- qqMText [toEnum 5]- @?? isLeft- qqMText [toEnum 127]- @?? isLeft- qqMText [toEnum 300]- @?? isLeft--unit_mt :: Assertion-unit_mt = do- [mt|aba|]- @?= MTextUnsafe "aba"- [mt| a |]- @?= MTextUnsafe " a "
− test/Test/Michelson/Typed/Util.hs
@@ -1,35 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Michelson.Typed.Util'.--module Test.Michelson.Typed.Util- ( unit_linearizeLeft_sample- , hprop_linearizeLeft_performance- ) where--import Hedgehog (Property)-import Test.HUnit (Assertion, (@?))--import Michelson.Test (meanTimeUpperBoundPropNF, sec)-import Michelson.Typed--unit_linearizeLeft_sample :: Assertion-unit_linearizeLeft_sample =- isLeftLinear (linearizeLeft (rightLinear 3)) @? "Failed to linearize"--isLeftLinear :: Instr inp out -> Bool-isLeftLinear (Seq _ (Seq {})) = False-isLeftLinear (Seq a _) = isLeftLinear a-isLeftLinear _ = True--rightLinear :: Word -> Instr '[ 'TUnit, 'TUnit ] '[ 'TUnit, 'TUnit]-rightLinear = go- where- go 0 = SWAP- go n = Seq SWAP (go (n - 1))--hprop_linearizeLeft_performance :: Property-hprop_linearizeLeft_performance =- meanTimeUpperBoundPropNF (sec 10) (linearizeLeft . rightLinear) 300000
− test/Test/Michelson/Typed/Value.hs
@@ -1,35 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests on 'Value'.-module Test.Michelson.Typed.Value- ( test_eqValueExt- ) where--import Test.HUnit (assertBool)-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)--import Michelson.Text-import Michelson.Typed.Haskell.Value-import Michelson.Typed.Value--test_eqValueExt :: [TestTree]-test_eqValueExt =- [ testCase "Simple" $- assertBool "Occured to be not reflexive" $- toVal @Integer 5 `eqValueExt` toVal @Integer 5-- , testCase "Complex" $- assertBool "Occured to be not reflexive" $- toVal ([mt|a|], 5 :: Integer)- `eqValueExt`- toVal ([mt|a|], 5 :: Integer)-- , testCase "Types do not matter if content is the same" $- -- Not sure this property is desired, leaving a test just for- -- illustration purposes- assertBool "Unexpectedly equal" . not $- toVal ([] @Integer) `eqValueExt` toVal ([] @MText)- ]
− test/Test/Morley/Expression.hs
@@ -1,167 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Morley.Micheline.Expression'.--module Test.Morley.Expression- ( test_Roundtrip_binary- , test_Roundtrip_expression- , test_Roundtrip_JSON- , test_toExpression- ) where--import Data.Aeson (FromJSON, ToJSON, eitherDecode, encode)-import qualified Data.Sequence as Seq-import Hedgehog (Gen)-import Test.Tasty (TestTree)--import Michelson.Test.Gen-import qualified Michelson.Typed as T-import qualified Michelson.Untyped as U-import qualified Morley.Micheline as M-import Test.Util.Hedgehog (roundtripTree)-import Tezos.Core (Mutez)--import Test.Hspec.Expectations (shouldBe)-import Test.Tasty.HUnit (testCase)--testJSON- :: forall a. (Eq a, Show a, ToJSON a, FromJSON a, Typeable a)- => Gen a -> TestTree-testJSON genA = roundtripTree @a genA encode eitherDecode--testValue- :: forall a. (Eq a, Show a, M.ToExpression a, M.FromExpression a, Typeable a)- => Gen a -> TestTree-testValue genA = roundtripTree @a genA M.toExpression (maybeToRight () . M.fromExpression)--testValueJSON- :: forall a. (Eq a, Show a, M.ToExpression a, M.FromExpression a, Typeable a)- => Gen a -> TestTree-testValueJSON genA = roundtripTree @a genA- (encode . M.toExpression)- (maybeToRight "error" . M.fromExpression <=< eitherDecode)--testBinary :: Gen M.Expression -> TestTree-testBinary genA = roundtripTree genA M.encodeExpression M.eitherDecodeExpression--test_Roundtrip_JSON :: [TestTree]-test_Roundtrip_JSON =- [ testJSON M.genExprAnnotation- , testJSON M.genExpression- , testJSON M.genMichelinePrimAp- , testValueJSON @(T.Value $ T.ToT Integer) genValueInt- , testValueJSON @(T.Value $ T.ToT Mutez) genValueMutez- , testValueJSON @(T.Value $ T.ToT ()) genValueUnit- ]--test_Roundtrip_binary :: [TestTree]-test_Roundtrip_binary =- [ testBinary M.genExpression- ]--test_Roundtrip_expression :: [TestTree]-test_Roundtrip_expression =- [ testValue @(T.Value $ T.ToT Integer) genValueInt- , testValue @(T.Value $ T.ToT Mutez) genValueMutez- , testValue @(T.Value $ T.ToT ()) genValueUnit- ]--test_toExpression :: [TestTree]-test_toExpression =- [ testCase "Converting TInt to Expression" $- M.toExpression T.TInt `shouldBe`- M.ExpressionPrim (M.MichelinePrimAp (M.MichelinePrimitive "int") mempty mempty)-- , testCase "Converting (TOption TString) to Expression" $- M.toExpression (T.TOption T.TString) `shouldBe`- M.ExpressionPrim- ( M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "option"- , mpaArgs = Seq.fromList [M.ExpressionPrim- ( M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "string"- , mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList [] }- )]- , mpaAnnots = Seq.fromList []- }- )-- , testCase "Converting Instr (DIP SIZE) to Expression" $- M.toExpression (T.DIP (T.SIZE @'T.TString)) `shouldBe`- M.ExpressionSeq (Seq.fromList- [ M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "DIP"- , mpaArgs = Seq.fromList [M.ExpressionSeq (Seq.fromList [M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "SIZE"- , mpaArgs = Seq.fromList []- , mpaAnnots = Seq.fromList []- })])]- , mpaAnnots = Seq.fromList []- })]- )- , testCase "Converting VInt to Expression" $- M.toExpression @(T.Value $ T.ToT Integer) (T.VInt 12) `shouldBe` M.ExpressionInt 12-- , testCase "Converting NTPair to Expression" $- M.toExpression (T.NTPair (U.ann "pair") (U.ann "a") (U.ann "b") (T.NTString U.noAnn) (T.NTInt $ U.ann "val")) `shouldBe`- M.ExpressionPrim- (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "pair"- , mpaArgs = Seq.fromList- [ M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "string"- , mpaArgs = Seq.fromList []- , mpaAnnots = Seq.fromList [M.AnnotationField (U.ann "a") ]})- , M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "int"- , mpaArgs = Seq.fromList []- , mpaAnnots = Seq.fromList [ M.AnnotationType (U.ann "val"), M.AnnotationField (U.ann "b")]})- ]- , mpaAnnots = Seq.fromList [ M.AnnotationType (U.ann "pair") ]- }- )- , testCase "Converting sample contract to Expression" $- M.toExpression sampleContract `shouldBe`- M.ExpressionSeq (Seq.fromList- [ M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "parameter"- , mpaArgs = Seq.fromList [M.ExpressionPrim (M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "unit", mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList [M.AnnotationField (U.ann "")]})]- , mpaAnnots = Seq.fromList []- })- , M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "storage"- , mpaArgs = Seq.fromList [M.ExpressionPrim (M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "chain_id", mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList []})]- , mpaAnnots = Seq.fromList []- })- , M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "code"- , mpaArgs = Seq.fromList [M.ExpressionSeq (Seq.fromList- [ M.ExpressionPrim (M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "DROP", mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList []})- , M.ExpressionPrim (M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "CHAIN_ID", mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList []})- , M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "NIL"- , mpaArgs = Seq.fromList- [ M.ExpressionPrim (M.MichelinePrimAp- { mpaPrim = M.MichelinePrimitive "operation"- , mpaArgs = Seq.fromList []- , mpaAnnots = Seq.fromList []- })- ]- , mpaAnnots = Seq.fromList []- })- , M.ExpressionPrim (M.MichelinePrimAp {mpaPrim = M.MichelinePrimitive "PAIR", mpaArgs = Seq.fromList [], mpaAnnots = Seq.fromList []})- ])]- , mpaAnnots = Seq.fromList []- })- ])- ]- where- sampleContract :: T.Contract 'T.TUnit 'T.TChainId- sampleContract =- let code = T.DROP `T.Seq` T.CHAIN_ID `T.Seq` T.NIL `T.Seq` T.PAIR- in T.Contract- { cCode = code- , cParamNotes = T.starParamNotes @'T.TUnit- , cStoreNotes = T.starNotes- }
− test/Test/OpSize.hs
@@ -1,277 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for operation size evaluation.--module Test.OpSize- ( test_Nesting- , test_Numbers- , test_Values- , test_Types- , test_Instructions- ) where--import Prelude hiding (EQ)--import qualified Data.Text as T-import Fmt (pretty)-import Hedgehog (Gen)-import Test.HUnit (Assertion, (@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Michelson.Macro-import Michelson.OpSize-import qualified Michelson.Parser as Parser-import Michelson.Test.Util (runGen)-import Michelson.Typed-import Michelson.Untyped (unsafeBuildEpName)-import Tezos.Address (genAddress)-import Tezos.Core-import Tezos.Crypto-import qualified Tezos.Crypto.Ed25519 as Ed25519-import qualified Tezos.Crypto.Secp256k1 as Secp256k1--import Test.Util.Parser--(?==) :: HasCallStack => Text -> Word -> Assertion-codeText ?== expectedCost = do- parsed <- Parser.codeEntry `shouldParse` ("{" <> codeText <> "}")- let code = expandList parsed- expandedInstrsOpSize code @?= OpSize expectedCost--{- All particular numbers below are got using dump-op-size.py script -}--test_Nesting :: [TestTree]-test_Nesting =- [ testCase "empty string" $ "" ?== 0- , testCase "{}" $ "{}" ?== 5- , testCase "{}; {}" $ "{}; {}" ?== 10- , testCase "100 * {}" $ mconcat (replicate 100 "{}; ") ?== 500- , testCase "{{}}" $ "{{}}" ?== 10- ]--test_Numbers :: [TestTree]-test_Numbers =- [ numTestCase 0 8- , numTestCase 1 8- , numTestCase 2 8- , numTestCase 63 8- , numTestCase 64 9- , numTestCase 65 9- , numTestCase 2048 9- , numTestCase 20480 10- , numTestCase (-1) 8- , numTestCase (-63) 8- , numTestCase (-64) 9- ]- where- numTestCase (n :: Int) gas =- testCase (show n) $ ("PUSH int " <> show n <> "; DROP") ?== gas--test_Values :: [TestTree]-test_Values =- [ testGroup "int"- [ valueTestCaseExt "small" "int" "5" 8- , valueTestCaseExt "bigger" "int" "63" 8- , valueTestCaseExt "big" "int" "64" 9- ]- , valueTestCase "nat" "5" 8- , testGroup "string"- [ stringTestCase 0 11- , stringTestCase 1 12- , stringTestCase 2 13- , stringTestCase 7 18- , stringTestCase 8 19- ]- , testGroup "bytes"- [ bytesTestCase 0 11- , bytesTestCase 1 12- , bytesTestCase 2 13- , bytesTestCase 7 18- , bytesTestCase 8 19- ]- , valueTestCase "mutez" "5" 8- , valueTestCase "bool" "True" 8- , valueTestCase "key_hash" (stringLike $ pretty (gen genKeyHash)) 47- , testGroup "timestamp"- [ valueTestCaseExt "small" "timestamp" "5" 8- , valueTestCaseExt "bigger" "timestamp" "123" 9- , valueTestCaseExt "textual" "timestamp" (stringLike "2018-08-08 00:00:00Z") 31- ]- , testGroup "address"- [ valueTestCaseExt "plain tz address" "address" (stringLike $ pretty (gen genAddress)) 47- , valueTestCaseExt "plain KT address" "address" (stringLike $ pretty (gen genAddress)) 47- , let addr = gen genAddress- in valueTestCaseExt "empty entrypoint" "address" (stringLike $ pretty addr <> "%") 48- , let epAddr = EpAddress(gen genAddress) (unsafeBuildEpName "a")- in valueTestCaseExt "short entrypoint" "address" (stringLike $ pretty epAddr) 49- , let epAddr = EpAddress (gen genAddress) (unsafeBuildEpName . mconcat $ replicate 8 "a")- in valueTestCaseExt "long entrypoint" "address" (stringLike $ pretty epAddr) 56- ]- -- ed25519 and secp256k1 keys have different size- , valueTestCase "key" (stringLike $ pretty (PublicKeyEd25519 (gen Ed25519.genPublicKey))) 65- , valueTestCase "key" (stringLike $ pretty (PublicKeySecp256k1 (gen Secp256k1.genPublicKey))) 66- , valueTestCase "unit" "Unit" 8- , valueTestCase "signature" (stringLike $ pretty (SignatureEd25519 (gen Ed25519.genSignature))) 110- , testGroup "chain_id"- [ valueTestCase "chain_id" (stringLike $ pretty dummyChainId) 26- , valueTestCase "chain_id" "0x00000000" 15- ]- , testGroup "option"- [ valueTestCaseExt "none" "(option int)" "None" 10- , valueTestCaseExt "some" "(option int)" "(Some 5)" 12- ]- , testGroup "list"- [ valueTestCaseExt "Empty" "(list int)" "{}" 13- , valueTestCaseExt "Length 1" "(list int)" "{1}" 15- , valueTestCaseExt "Length 8" "(list int)" "{1;1;1;1;1;1;1;1}" 29- ]- , testGroup "set"- [ valueTestCaseExt "Empty" "(set int)" "{}" 13- , valueTestCaseExt "Length 1" "(set int)" "{1}" 15- , valueTestCaseExt "Length 1" "(set int)" "{1;2;3;4;5;6;7;8}" 29- ]- , valueTestCaseExt "pair" "(pair int int)" "(Pair 1 2)" 16- , valueTestCaseExt "or" "(or int int)" "(Left 1)" 14- , testGroup "lambda"- [ valueTestCaseExt "empty lambda" "(lambda int int)" "{}" 15- , valueTestCaseExt "simple lambda" "(lambda int int)" "{DUP; DROP}" 19- ]- , testGroup "map"- [ valueTestCaseExt "Empty" "(map int int)" "{}" 15- , valueTestCaseExt "Length 1" "(map int int)" "{Elt 1 2}" 21- , valueTestCaseExt "Length 1 (big key)" "(map int int)" "{Elt 100 2}" 22- , let val = "{Elt 1 1; Elt 2 2; Elt 3 3; Elt 4 4; \- \ Elt 5 5; Elt 6 6; Elt 7 7; Elt 8 8 }"- in valueTestCaseExt "Length 8" "(map int int)" val 63- ]- ]- where- valueTestCaseExt name ty val gas =- testCase name $- ("PUSH " <> ty <> " " <> val <> "; DROP") ?== gas- valueTestCase ty val gas =- valueTestCaseExt (toString ty) ty val gas- stringTestCase l gas =- let name = "Length " <> show l- val = stringLike $ T.replicate l "a"- in valueTestCaseExt name "string" val gas- bytesTestCase l gas =- let name = "Length " <> show l- val = "0x" <> T.replicate l "12"- in valueTestCaseExt name "bytes" val gas-- stringLike x = "\"" <> x <> "\""- gen :: Gen a -> a- gen genA = runGen 20 12 genA--test_Types :: [TestTree]-test_Types =- [ typeTestCase "int" 8- , typeTestCase "string" 8- , typeTestCase "mutez" 8- , typeTestCase "key_hash" 8- , typeTestCase "address" 8- , typeTestCase "signature" 8- , typeTestCase "option int" 10- , typeTestCase "list int" 10- , typeTestCase "set int" 10- , typeTestCase "contract int" 10- , typeTestCase "pair int int" 12- , typeTestCase "or int int" 12- , typeTestCase "or (pair nat nat) (pair int nat)" 20- , typeTestCase "lambda int unit" 12- , typeTestCase "map int int" 12- , typeTestCase "big_map int int" 12- , 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- typeTestCase t gas =- testCase (show t) $ ("DUP; CONTRACT " <> t <> "; DROP") ?== gas--test_Instructions :: [TestTree]-test_Instructions =- [ instrTestCase "FAILWITH" 2- , instrTestCase "FAIL" 9-- , instrTestCaseFailWith "DUP" 4- , instrTestCaseFailWith "DUP @" 4- , instrTestCaseFailWith "DUP @a" 10- , instrTestCaseFailWith "DUP; DROP" 6- , instrTestCaseFailWith "DUP; SWAP" 6- , instrTestCaseFailWith "DUP; SWAP; SWAP" 8- , instrTestCaseFailWith "PUSH int 0" 8- , instrTestCaseFailWith "PUSH @a int 0" 14- , instrTestCaseFailWith "PUSH @a (int :a) 0" 20- , instrTestCaseFailWith "SOME" 4- , instrTestCaseFailWith "SOME @" 4- , instrTestCaseFailWith "SOME @a" 10- , instrTestCaseFailWith "NONE int" 6- , instrTestCaseFailWith "NONE (option int)" 8- , instrTestCaseFailWith "NONE :a @a (option :a int)" 23- , instrTestCaseFailWith "UNIT" 4- , instrTestCaseFailWith "DUP; SOME; IF_NONE {}{DROP}" 20- , instrTestCaseFailWith "DUP; DUP; PAIR" 8- , instrTestCaseFailWith "DUP; DUP; PAIR % % : @" 8- , instrTestCaseFailWith "DUP; DUP; PAIR %a" 14- , instrTestCaseFailWith "DUP; DUP; PAIR %a %a" 17- , instrTestCaseFailWith "DUP; DUP; PAIR %a %a :a" 20- , instrTestCaseFailWith "DUP; DUP; PAIR %a % :a @a" 20- , instrTestCaseFailWith "DUP; DUP; PAIR % %a :a @a" 23- , instrTestCaseFailWith "DUP; DUP; PAIR %a %a :a @a" 23- -- Further skipping some instructions since everything is trivial- , instrTestCaseFailWith "EMPTY_BIG_MAP :a @a (int :a) (unit :a)" 29- , instrTestCaseFailWith "PUSH (list int) {}; MAP {}" 20- , instrTestCaseFailWith "DIP {}" 9- , instrTestCaseFailWith "DIP 0 {}" 11- , instrTestCaseFailWith "DIP 1 {}" 11- , instrTestCaseFailWith "DROP 0" 6- , instrTestCaseFailWith "PACK" 4- , instrTestCaseFailWith "PUSH int 1; PUSH int 2; ADD" 16- , instrTestCaseFailWith "DUP; CONTRACT int" 8-- , namedInstrTestCaseFailWith "CREATE_CONTRACT"- "UNIT; PUSH mutez 5; NONE key_hash; \- \CREATE_CONTRACT \- \{ parameter unit; \- \ storage unit; \- \ code { CDR; NIL operation; PAIR } }" 44-- , namedInstrTestCaseFailWith "CREATE_CONTRACT + PUSH or"- "PUSH (or int nat) (Left 5); PUSH mutez 5; NONE key_hash; \- \CREATE_CONTRACT \- \{ parameter unit; \- \ storage (or int nat); \- \ code { DROP; PUSH (or int nat) (Right 2); NIL operation; PAIR } }" 70- , namedInstrTestCaseFailWith "CREATE_CONTRACT + PUSH or + Annotation"- "PUSH (or %root int nat) (Left 5); PUSH mutez 5; NONE key_hash; \- \CREATE_CONTRACT \- \{ parameter unit; \- \ storage (or int nat); \- \ code { DROP; PUSH (or int nat) (Right 2); NIL operation; PAIR } }" 70- ]- where- namedInstrTestCase testName instr gas =- testCase testName $ instr ?== gas-- namedInstrTestCaseFailWith testName instr gas =- namedInstrTestCase testName (instr <> "; FAILWITH") gas-- instrTestCase instr gas =- namedInstrTestCase (show instr) instr gas-- instrTestCaseFailWith instr gas =- instrTestCase (instr <> "; FAILWITH") gas
− test/Test/Optimizer.hs
@@ -1,163 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for optimizer.--module Test.Optimizer- ( 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)--import Data.Default (def)-import Test.HUnit (Assertion, (@?=))--import Michelson.Interpret.Pack (packValue')-import Michelson.Optimizer-import Michelson.Text-import qualified Michelson.Typed as T-import Michelson.Typed.Instr-import Util.Peano (SingNat(..))---- Sample stacks of length 0, 1…-type Stack0 = '[ ]-type Stack1 = '[ 'T.TUnit ]-type Stack1Int = '[ 'T.TInt ]-type Stack1Pair = '[ 'T.TPair 'T.TUnit 'T.TUnit ]-type Stack2 = '[ 'T.TUnit, 'T.TUnit ]-type Stack2UnitInt = '[ 'T.TUnit, 'T.TInt ]--unit_Optimizer_Tree_Independence :: Assertion-unit_Optimizer_Tree_Independence = do- optimize @Stack1Pair @Stack1 (DUP `Seq` (CAR `Seq` (DIP CDR `Seq` DROP))) @?= (DUP `Seq` CAR `Seq` DROP `Seq` CDR)- optimize @Stack1Pair @Stack1 ((DUP `Seq` CAR) `Seq` (DIP CDR `Seq` DROP)) @?= (DUP `Seq` CAR `Seq` DROP `Seq` CDR)- optimize @Stack1Pair @Stack1 (((DUP `Seq` CAR) `Seq` DIP CDR) `Seq` DROP) @?= (DUP `Seq` CAR `Seq` DROP `Seq` CDR)--unit_Optimize_DROP_n :: Assertion-unit_Optimize_DROP_n = do- optimize @Stack0 @Stack0 (DROPN SZ) @?= Nop- -- Sadly it is not optimized (yet).- optimize @Stack1 @Stack0 (DROPN (SS SZ)) @?= DROPN (SS SZ)--unit_Optimize_DIP_n :: Assertion-unit_Optimize_DIP_n = do- optimize @Stack1 @Stack0 (DIPN SZ DROP) @?= DROP- -- Sadly it is not optimized (yet).- optimize @Stack1 @Stack2 (DIPN (SS SZ) UNIT) @?= (DIPN (SS SZ) UNIT)--unit_Redundant_DIP :: Assertion-unit_Redundant_DIP = do- 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 @'T.TInt @'T.TUnit mempty)) @?= EMPTY_MAP- optimize (PUSH (T.VSet @'T.TInt mempty) `Seq` NOW) @?= (EMPTY_SET `Seq` NOW)- optimize (PUSH T.VUnit) @?= UNIT--unit_Optimize_PUSH_PACK :: Assertion-unit_Optimize_PUSH_PACK =- optimize'- (PUSH strValue `Seq` PACK `Seq` DUP) @?=- (PUSH (T.VBytes $ packValue' strValue) `Seq` DUP)- where- optimize' =- optimizeWithConf @Stack0 @'[ 'T.TBytes, 'T.TBytes ]- (def {ruleset = defaultRulesAndPushPack})--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.TString-strValue = T.VString str--nonOptimal :: T.ContractCode 'T.TString 'T.TString-nonOptimal =- CAR `Seq`- -- `PUSH; DROP` is erased- -- We also arbitrarily group two instructions here to make- -- structure definitely non-linear.- (PUSH strValue `Seq` SWAP `Seq` SWAP `Seq` DROP) `Seq`- -- If we PUSH and then DIP, DIP is not necessary- PUSH strValue `Seq`- -- `DUP; DROP` is also erased- DIP (DUP `Seq` DUP `Seq` DROP) `Seq`- -- `SWAP; SWAP` is erased, along with surrounding redundant instructions and outer `DIP`- DIP (PUSH (T.VBool False) `Seq` IF (Nop) (SWAP `Seq` SWAP)) `Seq`- CONCAT `Seq`- Nested (SIZE `Seq`- -- `COMPARE` with 0 is redundant- (PUSH (T.VNat 0) `Seq` COMPARE) `Seq` EQ `Seq`- -- Here both bodys of `IF` can be erased and then `IF` can be replaced with `DROP`- IF (DUP `Seq` DROP) (UNIT `Seq` DROP) `Seq`- -- `LEFT` followed by `IF_LEFT` can be optimized- LEFT @('T.TKey) `Seq` IF_LEFT Nop (UNIT `Seq` FAILWITH) `Seq`- -- SWAP is redundant after DUP- DUP `Seq` SWAP `Seq` CONCAT `Seq`- -- `DIP Nop` is thrown away- DIP (UNIT `Seq` DROP) `Seq`- -- Finish, nothing to optimize here- NIL `Seq` PAIR)---- Auxiliary operator to produce right linear sequence. We do not use--- it above because input instruction can have arbitrary structure,--- but we know that the output is right balanced. In practice we can't--- check it though, because that's how our 'Eq' is defined.-(#<#) :: T.Instr a b -> T.Instr b c -> T.Instr a c-(#<#) = Seq-infixr 1 #<#---- Expected output of the optimizer.-expectedOptimized :: T.ContractCode 'T.TString 'T.TString-expectedOptimized =- CAR #<#- DUP #<#- PUSH strValue #<#- CONCAT #<#- SIZE #<#- INT #<# EQ #<#- DROP #<#- DUP #<# CONCAT #<#- NIL #<# PAIR
− test/Test/Parser.hs
@@ -1,312 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Parser- ( unit_Parse_contracts- , unit_Parse_bad_contracts- , unit_Value- , unit_string_literal- , unit_annotation- , unit_IF- , unit_MAP- , unit_PAIR- , unit_pair_type- , unit_tuple_type- , unit_or_type- , unit_lambda_type- , unit_list_type- , unit_set_type- , unit_Pair_constructor- , unit_PrintComment- , unit_ParserException- , unit_letType- , unit_block_comment- ) where--import qualified Data.List.NonEmpty as NE-import Test.Hspec.Expectations (Expectation, expectationFailure, shouldBe, shouldSatisfy)-import Text.Megaparsec (parse)-import Text.Megaparsec.Error- (ErrorFancy(ErrorCustom), ErrorItem(Tokens), ParseError(FancyError, TrivialError), bundleErrors)--import Michelson.ErrorPos (srcPos)-import Michelson.Macro as Mo-import Michelson.Parser as P-import Michelson.Parser.Annotations as PA-import Michelson.Parser.Lexer as PL-import Michelson.Untyped as Mo-import Util.IO-import Util.Positive--import Test.Util.Contracts (getIllTypedContracts, getUnparsableContracts, getWellTypedContracts)--unit_Parse_contracts :: Expectation-unit_Parse_contracts = do- files <- mappend <$> getWellTypedContracts <*> getIllTypedContracts- mapM_ (checkFile True) files--unit_Parse_bad_contracts :: Expectation-unit_Parse_bad_contracts = do- files <- getUnparsableContracts- mapM_ (checkFile False) files--checkFile :: Bool -> FilePath -> Expectation-checkFile shouldParse file = do- code <- readFileUtf8 file- case parse P.program file code of- Left err- | shouldParse -> expectationFailure $ errorBundlePretty err- Right _- | not shouldParse -> expectationFailure $ "Managed to parse " <> file- _ -> pass--unit_Value :: Expectation-unit_Value = do- P.parseNoEnv P.value "" "{}" `shouldBe`- Right Mo.ValueNil- P.parseNoEnv P.value "" "{PUSH int 5;}" `shouldBe`- (Right . ValueLambda $ NE.fromList- [Mo.Prim (Mo.PUSH noAnn (Mo.Type Mo.TInt noAnn) (Mo.ValueInt 5)) (srcPos 0 1)]- )- P.parseNoEnv P.value "" "{1; 2}" `shouldBe`- (Right . Mo.ValueSeq $ NE.fromList- [Mo.ValueInt 1, Mo.ValueInt 2]- )- P.parseNoEnv P.value "" "{Elt 1 2; Elt 3 4}" `shouldBe`- (Right . Mo.ValueMap $ NE.fromList- [Mo.Elt (Mo.ValueInt 1) (Mo.ValueInt 2), Mo.Elt (Mo.ValueInt 3) (Mo.ValueInt 4)]- )- P.parseNoEnv P.value "" "{DIP DROP;}" `shouldBe`- Right (Mo.ValueLambda (Mo.Prim (Mo.DIP [Mo.Prim Mo.DROP (srcPos 0 5)]) (srcPos 0 1) :| []))- P.parseNoEnv P.value "" "{DIP DROP;somecontent}" `shouldSatisfy` isLeft- P.parseNoEnv P.value "" "{{ }; {}; {PUSH int 5; DROP}}" `shouldBe`- Right (Mo.ValueLambda (Mo.Seq [] (srcPos 0 1)- :| [ Mo.Seq [] (srcPos 0 6)- , Mo.Seq [Mo.Prim (Mo.PUSH noAnn (Mo.Type Mo.TInt noAnn) (Mo.ValueInt 5)) (srcPos 0 11)- , Mo.Prim Mo.DROP (srcPos 0 23)] (srcPos 0 10)- ]- ))- P.parseNoEnv P.value "" "{{}; {}; {5}}" `shouldBe`- Right (Mo.ValueSeq (Mo.ValueNil :| [Mo.ValueNil, Mo.ValueSeq (Mo.ValueInt 5 :| [])]))- P.parseNoEnv P.value "" "{{}; {5}; {Push int 5}}" `shouldSatisfy` isLeft--unit_string_literal :: Expectation-unit_string_literal = do- P.parseNoEnv P.stringLiteral "" "\"\"" `shouldSatisfy` isRight- P.parseNoEnv P.stringLiteral "" "\" \\n \"" `shouldSatisfy` isRight- P.parseNoEnv P.stringLiteral "" "\"abacaba \\t \n\n\r a\"" `shouldSatisfy` isLeft- P.parseNoEnv P.stringLiteral "" "\"abacaba \\t \\n\\n\\r" `shouldSatisfy` isLeft--unit_annotation :: Expectation-unit_annotation = do- P.parseNoEnv PA.noteV "" "@" `shouldSatisfy` isRight- P.parseNoEnv PA.noteV "" "@_" `shouldSatisfy` isRight- P.parseNoEnv PA.noteV "" "@a." `shouldSatisfy` isRight- P.parseNoEnv PA.noteV "" "@7a" `shouldSatisfy` isRight- P.parseNoEnv PA.noteV "" "@.a" `shouldSatisfy` isLeft- P.parseNoEnv PA.noteV "" "@@@" `shouldSatisfy` isLeft- P.parseNoEnv PA.noteV "" "@a b" `shouldSatisfy` isLeft- P.parseNoEnv PA.noteV "" "@a\\" `shouldSatisfy` isLeft- -- TODO [#48] these are special annotations and should not always be accepted- P.parseNoEnv PA.noteV "" "@%" `shouldSatisfy` isRight- P.parseNoEnv PA.noteV "" "@%%" `shouldSatisfy` isRight--unit_IF :: Expectation-unit_IF = do- P.parseNoEnv P.codeEntry "" "{IF {} {};}" `shouldBe`- Prelude.Right [Mo.Prim (Mo.IF [] []) (srcPos 0 1)]- P.parseNoEnv P.codeEntry "" "{IFEQ {} {};}" `shouldBe`- Prelude.Right [Mo.Mac (Mo.IFX (Mo.EQ noAnn) [] []) (srcPos 0 1)]- P.parseNoEnv P.codeEntry "" "{IFCMPEQ {} {};}" `shouldBe`- Prelude.Right [Mo.Mac (Mo.IFCMP (Mo.EQ noAnn) noAnn [] []) (srcPos 0 1)]--unit_MAP :: Expectation-unit_MAP = do- parseNoEnv P.codeEntry "" "{MAP {};}" `shouldBe`- Prelude.Right [Mo.Prim (Mo.MAP noAnn []) (srcPos 0 1)]- parseNoEnv P.codeEntry "" "{MAP_CAR {};}" `shouldBe`- Prelude.Right [Mo.Mac (Mo.MAP_CADR [Mo.A] noAnn noAnn []) (srcPos 0 1)]--unit_PAIR :: Expectation-unit_PAIR = do- P.parseNoEnv P.codeEntry "" "{PAIR;}" `shouldBe`- Prelude.Right [Mo.Prim (PAIR noAnn noAnn noAnn noAnn) (srcPos 0 1)]- P.parseNoEnv P.codeEntry "" "{PAIR %a;}" `shouldBe`- Prelude.Right [Mac (PAPAIR (P (F (noAnn, Mo.ann "a")) (F (noAnn,noAnn))) noAnn noAnn) (srcPos 0 1)]- P.parseNoEnv P.codeEntry "" "{PAIR %0;}" `shouldBe`- Prelude.Right [Mac (PAPAIR (P (F (noAnn, Mo.ann "0")) (F (noAnn,noAnn))) noAnn noAnn) (srcPos 0 1)]- P.parseNoEnv P.codeEntry "" "{PAPAIR;}" `shouldBe`- Prelude.Right- [flip Mac (srcPos 0 1) $- PAPAIR (P (F (noAnn,noAnn)) (P (F (noAnn,noAnn)) (F (noAnn,noAnn))))- noAnn noAnn- ]--unit_pair_type :: Expectation-unit_pair_type = do- P.parseNoEnv P.type_ "" "pair unit unit" `shouldBe` Right unitPair- P.parseNoEnv P.type_ "" "(unit, unit)" `shouldBe` Right unitPair- P.parseNoEnv P.type_ "" "(key, (int, (string, bool)))"- `shouldSatisfy` isRight- P.parseNoEnv P.type_ "" "(signature, chain_id, string, bool)"- `shouldSatisfy` isRight- where- unitPair :: Mo.Type- unitPair =- Mo.Type (Mo.TPair noAnn noAnn (Mo.Type Mo.TUnit noAnn) (Mo.Type Mo.TUnit noAnn)) noAnn--unit_tuple_type :: Expectation-unit_tuple_type = do- P.parseNoEnv P.type_ "" "(int, int, bool, unit, nat)"- `shouldBe` Right (typair (typair tyint tyint) (typair tybool (typair tyunit tynat)))- P.parseNoEnv P.type_ ""- "(pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat (pair nat nat))))))))))))))))"- `shouldBe` Right (bigPair 16)- where- bigPair :: Integer -> Type- bigPair n | n == 0 = tynat- bigPair n = typair tynat (bigPair (n - 1))--unit_or_type :: Expectation-unit_or_type = do- P.parseNoEnv P.type_ "" "or unit unit" `shouldBe` Right unitOr- P.parseNoEnv P.type_ "" "(unit | unit)" `shouldBe` Right unitOr- P.parseNoEnv P.type_ "" "(chain_id | (int | (string | bool)))"- `shouldSatisfy` isRight- P.parseNoEnv P.type_ "" "or unit unit kek"- `shouldSatisfy` isLeft- where- unitOr :: Mo.Type- unitOr =- Mo.Type (Mo.TOr noAnn noAnn (Mo.Type Mo.TUnit noAnn) (Mo.Type Mo.TUnit noAnn)) noAnn--unit_lambda_type :: Expectation-unit_lambda_type = do- P.parseNoEnv P.type_ "" "lambda unit unit" `shouldBe` Right lambdaUnitUnit- P.parseNoEnv P.type_ "" "\\unit -> unit" `shouldBe` Right lambdaUnitUnit- P.parseNoEnv P.type_ "" "lambda int (signature, int)" `shouldSatisfy` isRight- where- lambdaUnitUnit :: Mo.Type- lambdaUnitUnit =- Mo.Type (Mo.TLambda (Mo.Type Mo.TUnit noAnn) (Mo.Type Mo.TUnit noAnn)) noAnn--unit_list_type :: Expectation-unit_list_type = do- P.parseNoEnv P.type_ "" "list unit" `shouldBe` Right unitList- P.parseNoEnv P.type_ "" "[unit]" `shouldBe` Right unitList- P.parseNoEnv P.type_ "" "[(key, key)]" `shouldSatisfy` isRight- where- unitList :: Mo.Type- unitList =- Mo.Type (Mo.TList (Mo.Type Mo.TUnit noAnn)) noAnn--unit_set_type :: Expectation-unit_set_type = do- P.parseNoEnv P.type_ "" "set int" `shouldBe` Right intSet- P.parseNoEnv P.type_ "" "set (pair int int)" `shouldBe` Right intPairSet- P.parseNoEnv P.type_ "" "{int}" `shouldBe` Right intSet- P.parseNoEnv P.type_ "" "{(pair int int)}" `shouldBe` Right intPairSet- where- intSet :: Mo.Type- intSet =- Mo.Type (Mo.TSet (Mo.Type Mo.TInt noAnn)) noAnn- intPairSet :: Mo.Type- intPairSet =- Mo.Type (Mo.TSet (Mo.Type (Mo.TPair noAnn noAnn (Mo.Type Mo.TInt noAnn) (Mo.Type Mo.TInt Mo.noAnn)) noAnn)) noAnn--unit_Pair_constructor :: Expectation-unit_Pair_constructor = do- P.parseNoEnv P.value "" "Pair Unit Unit" `shouldBe` Right unitPair- P.parseNoEnv P.value "" "(Unit, Unit)" `shouldBe` Right unitPair- where- unitPair :: Mo.Value' Mo.ParsedOp- unitPair = Mo.ValuePair Mo.ValueUnit Mo.ValueUnit--unit_PrintComment :: Expectation-unit_PrintComment = do- P.parseNoEnv P.printComment "" "\"Sides are %[0] x %[1]\"" `shouldBe`- Right (PrintComment [Left "Sides are ", Right (StackRef 0), Left " x ", Right (StackRef 1)])- P.parseNoEnv P.printComment "" "\"%[0] x\"" `shouldBe`- Right (PrintComment [Right (StackRef 0), Left " x"])- P.parseNoEnv P.printComment "" "\"%[0]x%[1]\"" `shouldBe`- Right (PrintComment [Right (StackRef 0), Left "x", Right (StackRef 1)])- P.parseNoEnv P.printComment "" "\"%[0]%[1]\"" `shouldBe`- Right (PrintComment [Right (StackRef 0), Right (StackRef 1)])- P.parseNoEnv P.printComment "" "\"xxx\"" `shouldBe`- Right (PrintComment [Left "xxx"])- P.parseNoEnv P.printComment "" "\"\"" `shouldBe`- Right (PrintComment [])--unit_ParserException :: Expectation-unit_ParserException = do- handleCustomError "0x000" P.value OddNumberBytesException- handleCustomError "Right 0x000" P.value OddNumberBytesException- handleCustomError "kek" P.type_ UnknownTypeException- handleCustomError "\"aaa\\r\"" P.stringLiteral- (StringLiteralException (InvalidEscapeSequence 'r'))- handleCustomError "\"aaa\\b\"" P.stringLiteral- (StringLiteralException (InvalidEscapeSequence 'b'))- handleCustomError "\"aaa\\t\"" P.stringLiteral- (StringLiteralException (InvalidEscapeSequence 't'))- handleCustomError "\"aaa\n\"" P.stringLiteral- (StringLiteralException (InvalidChar '\n'))- handleCustomError "\"aaa\r\"" P.stringLiteral- (StringLiteralException (InvalidChar '\r'))- handleCustomError "{ TAG 2 (int | string) }" P.codeEntry- (WrongTagArgs 2 (PositiveUnsafe 2))- handleCustomError "{ ACCESS 2 2 }" P.codeEntry- (WrongAccessArgs 2 (PositiveUnsafe 2))- handleCustomError "{ SET 2 2 }" P.codeEntry- (WrongSetArgs 2 (PositiveUnsafe 2))- handleTrivialError "type Store = (BigMap Address Nat, Nat); \ntest :: '[option int] -> '[int]\n= { IF_SOME { nop; } { PUSH int 3 }; };"- (P.letInner P.parsedOp)- (Tokens ('n' :| ""))-- where- handleCustomError- :: HasCallStack => Text -> Parser a -> CustomParserException -> Expectation- handleCustomError text parser customException =- case P.parseNoEnv parser "" text of- Right _ -> expectationFailure "expecting parser to fail"- Left bundle -> case toList $ bundleErrors bundle of- [FancyError _ (toList -> [ErrorCustom e])] ->- e `shouldBe` customException- _ ->- expectationFailure $- "expecting single ErrorCustom, but got " <>- errorBundlePretty bundle-- handleTrivialError- :: HasCallStack => Text -> Parser a -> ErrorItem Char -> Expectation- handleTrivialError text parser errorItem =- case P.parseNoEnv parser "" text of- Right _ -> expectationFailure "expecting parser to fail"- Left bundle -> case toList $ bundleErrors bundle of- [TrivialError _ e _] ->- e `shouldBe` (Just errorItem)- _ ->- expectationFailure $- "expecting single TrivialError, but got " <>- errorBundlePretty bundle--unit_letType :: Expectation-unit_letType = do- P.parseNoEnv P.letType "" "type kek = int" `shouldSatisfy` isRight- -- They used to be prohibited, but now we permit them.- P.parseNoEnv P.letType "" "type Parameter = int" `shouldSatisfy` isRight- P.parseNoEnv P.letType "" "type Storage = int" `shouldSatisfy` isRight--unit_block_comment :: Expectation-unit_block_comment =- forM_ validBlockComments $ \blockComment ->- P.parseNoEnv PL.mSpace "" blockComment `shouldBe` Right ()- where- validBlockComments =- [ "/* */"- , " /* */"- , "/* */ "- , "/* */;"- , "/* */ ;"- , "/* */\n;"- ]
− test/Test/Preprocess.hs
@@ -1,60 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for preprocessing.--module Test.Preprocess- ( unit_Sample_preprocess- ) where--import Prelude hiding (EQ)--import Test.HUnit (Assertion, (@?=))--import Michelson.Preprocess-import Michelson.Text-import qualified Michelson.Typed as T-import Michelson.Typed.Instr--unit_Sample_preprocess :: Assertion-unit_Sample_preprocess = do- transformStrings False (f (mkMTextUnsafe . toText)) (sample (T.VString . mkMTextUnsafe)) @?= (expected (T.VString . mkMTextUnsafe))- transformBytes False (f fromString) (sample T.VBytes) @?= (expected T.VBytes)- where- f :: (Eq a) => (String -> a) -> a -> a- f fn s- | s == (fn str1) = (fn str5)- | s == (fn str2) = (fn str4)- | s == (fn str4) = (fn str2)- | s == (fn str5) = (fn str1)- | otherwise = s--sample :: (IsString i, T.ConstantScope o, Typeable o) => (i -> T.Value o) -> T.ContractCode o o-sample fn =- CAR `Seq`- PUSH (fn str1) `Seq`- DIP (PUSH (fn str2)) `Seq`- LEFT @('T.TKey) `Seq` IF_LEFT Nop (PUSH (fn str4) `Seq` FAILWITH) `Seq`- DIP (DIP (PUSH (fn str5))) `Seq`- PUSH (fn str3) `Seq`- DROP `Seq` DROP `Seq` DROP `Seq` DROP `Seq`- NIL `Seq` PAIR--expected :: (IsString i, T.ConstantScope o, Typeable o) => (i -> T.Value o) -> T.ContractCode o o-expected fn =- CAR `Seq`- PUSH (fn str5) `Seq`- DIP (PUSH (fn str4)) `Seq`- LEFT @('T.TKey) `Seq` IF_LEFT Nop (PUSH (fn str2) `Seq` FAILWITH) `Seq`- DIP (DIP (PUSH (fn str1))) `Seq`- PUSH (fn str3) `Seq`- DROP `Seq` DROP `Seq` DROP `Seq` DROP `Seq`- NIL `Seq` PAIR--str1, str2, str3, str4, str5 :: (IsString a) => a-str1 = "aa"-str2 = "ls"-str3 = "gulya"-str4 = "naiks"-str5 = "eek"
− test/Test/Printer/Michelson.hs
@@ -1,131 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# LANGUAGE DeriveAnyClass #-}--module Test.Printer.Michelson- ( unit_Roundtrip- , unit_let_macro- , unit_PrettyPrint- , unit_PrintTypedNotes- , unit_PrintSmartParens- , unit_PrintBigDipN- ) where--import qualified Data.Map.Lazy as Map-import Data.Text.Lazy (strip)-import Fmt (pretty)-import Generics.SYB (everywhere, mkT)-import Test.HUnit (Assertion, assertEqual, assertFailure, (@?=))--import Michelson.Printer (printSomeContract, printUntypedContract)-import Michelson.Runtime (parseExpandContract)-import Michelson.TypeCheck.Instr (typeCheckContract)--import Michelson.Test (importUntypedContract)-import qualified Michelson.Untyped as U-import Michelson.Untyped.Instr (ExpandedOp(..))-import Util.IO (readFileUtf8)--import Test.Util.Contracts-import Test.Util.HUnit---- | Check that contract under the first given file, when parsed and printed--- back, produces the contract under the second file.-printerTest :: (FilePath, FilePath) -> Assertion-printerTest (srcPath, refPath) = do- contract <- importUntypedContract srcPath- case typeCheckContract Map.empty contract of- Left err -> assertFailure ("Failed to typecheck contract " <> srcPath <> ": " <> pretty err)- Right checkedContract -> do- targetSrc <- strip . fromStrict <$> readFileUtf8 refPath- assertEqualBuild- ("Prettifying " <> srcPath <> " does not match the expected format")- targetSrc- (printSomeContract False checkedContract)--unit_PrintTypedNotes :: Assertion-unit_PrintTypedNotes = do- contracts <- getContractsWithReferences ".tz" (inContractsDir "notes-in-typed-contracts") "ref"- mapM_ printerTest contracts--unit_PrintSmartParens :: Assertion-unit_PrintSmartParens = do- 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" (inContractsDir "pretty") "pretty"- mapM_ prettyTest contracts- where- prettyTest :: (FilePath, FilePath) -> Assertion- prettyTest (srcPath, refPath) = do- contract <- importUntypedContract srcPath- targetSrc <- strip . fromStrict <$> readFileUtf8 refPath- assertEqual- ("Prettifying " <> srcPath <> " does not match the expected format")- (printUntypedContract False contract)- targetSrc- assertEqual- ("Single line pretty printer output "- <> srcPath <> " contain new lines.")- (find (=='\n') $ printUntypedContract True contract) Nothing--unit_Roundtrip :: Assertion-unit_Roundtrip = do- morleyContractFiles <- getWellTypedMorleyContracts- mapM_ morleyRoundtripPrintTest morleyContractFiles- michelsonContractFiles <- getWellTypedMichelsonContracts- mapM_ michelsonRoundtripPrintTest michelsonContractFiles- where- morleyRoundtripPrintTest :: FilePath -> Assertion- morleyRoundtripPrintTest filePath = do- contract1 <- importUntypedContract filePath- contract2 <- printAndParse filePath contract1- -- We don't expect that `contract1` equals `contract2`,- -- because during printing we lose extra instructions.- assertEqual ("After printing and parsing " <> filePath <>- " is printed differently")- (printUntypedContract True contract1) -- using single line output here- (printUntypedContract True contract2)- michelsonRoundtripPrintTest :: FilePath -> Assertion- michelsonRoundtripPrintTest filePath = do- contract1 <- importUntypedContract filePath- contract2 <- printAndParse filePath contract1- -- We expect `contract1` equals `contract2`.- assertEqual ("After printing and parsing " <> filePath <>- " contracts are different")- (transformContract contract1) (transformContract contract2)--unit_let_macro :: Assertion-unit_let_macro = do- let filePath = inContractsDir "ill-typed/letblock_trivial.mtz"- contract <- printAndParse filePath =<< importUntypedContract filePath- let ops = concatMap U.flattenExpandedOp (U.contractCode contract)- ops @?= [U.CDR U.noAnn U.noAnn, U.UNIT U.noAnn U.noAnn, U.DROP]--printAndParse :: FilePath -> U.Contract -> IO U.Contract-printAndParse fp contract1 =- case parseExpandContract (Just fp) (toText $ printUntypedContract True contract1) of- Left err ->- assertFailure ("Failed to parse printed " <> fp <> ": " <> pretty err)- Right contract2 -> pure contract2---- | Remove all `WithSrcEx` from contract code because `SrcPos`es--- and such stuff can change during printing and parsing-transformContract :: U.Contract -> U.Contract-transformContract (U.Contract c s code) =- U.Contract c s (map transform code)- where- transform :: ExpandedOp -> ExpandedOp- transform = everywhere $ mkT removeWithSrcEx- removeWithSrcEx :: ExpandedOp -> ExpandedOp- removeWithSrcEx (WithSrcEx _ op) = op- removeWithSrcEx op = op
− test/Test/Serialization/Aeson.hs
@@ -1,52 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Serialization.Aeson- ( test_Roundtrip- ) where--import Data.Aeson (FromJSON, ToJSON, eitherDecode, encode)-import Hedgehog (Gen)-import Test.Tasty (TestTree)--import Michelson.Test.Gen (genMutez, genTimestamp)-import Michelson.Untyped- (Contract, Elt, ExpandedOp, FieldAnn, InstrAbstract, StackFn, TypeAnn, Value, VarAnn)-import Tezos.Core (Mutez, Timestamp)-import Util.Test.Gen--import Test.Util.Hedgehog (roundtripTree)----- Note: if we want to enforce a particular JSON format, we can extend--- these test with golden tests (it's easy with `hspec-golden-aeson`).--test- :: forall a. (Eq a, Show a, ToJSON a, FromJSON a, Typeable a)- => Gen a -> TestTree-test genA = roundtripTree @a genA encode eitherDecode--test_Roundtrip :: [TestTree]-test_Roundtrip =- [ -- Core Tezos types- test @Timestamp genTimestamp- , test @Mutez genMutez-- -- Michelson types- , test @StackFn genStackFn- , test @ExpandedOp genExpandedOp-- -- these are actually all the same thing (Annotation a),- -- where a is a phantom type,- -- but let's test them in case they- -- ever change for some reason- , test @TypeAnn genAnnotation- , test @FieldAnn genAnnotation- , test @VarAnn genAnnotation-- , test @Contract genContract- , test @(InstrAbstract ExpandedOp) (genInstrAbstract genExpandedOp)- , test @Value genValue- , test @(Elt ExpandedOp) (genElt genExpandedOp)- ]
− test/Test/Serialization/Michelson.hs
@@ -1,1093 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# LANGUAGE OverloadedLists #-}--module Test.Serialization.Michelson- ( spec_Packing- ) where--import Prelude hiding (Ordering(..))--import qualified Data.Text as T-import Data.Typeable ((:~:)(..), eqT, typeRep)-import Test.Hspec (Expectation, Spec, describe, it, shouldBe, shouldSatisfy)-import Text.Hex (decodeHex, encodeHex)--import Michelson.Interpret (runUnpack)-import Michelson.Interpret.Pack (packValue')-import Michelson.Interpret.Unpack (unpackValue')-import Michelson.Macro (expandList)-import qualified Michelson.Parser as Parser-import Michelson.Test.Util-import Michelson.Text-import Michelson.TypeCheck (HST(..), SomeInstr(..), SomeInstrOut(..), getWTP, typeCheckList)-import Michelson.Typed-import Michelson.Untyped (noAnn)-import Test.Util.Parser-import Tezos.Address (Address(..), unsafeParseAddress)-import Tezos.Core (ChainId(..), Mutez, Timestamp, parseChainId, timestampFromSeconds, unsafeMkMutez)-import Tezos.Crypto (KeyHash(..), parseKeyHash, parsePublicKey, parseSignature)--spec_Packing :: Spec-spec_Packing = do- describe "pack tests for comparable values (CValue)" $ do- intTest- natTest- stringTest- bytesTest- mutezTest- boolTest- keyHashTest- timestampTest- addressTest-- describe "pack tests for non-comparable values" $ do- keyTest- unitTest- signatureTest- chainIdTest- optionTest- listTest- setTest- contractTest- pairTest- orTest- mapTest-- describe "pack tests for instructions" $ do- instrTest- typesTest-- unpackNegTest- unpackVarAnnTest- readableUnpackTest- lengthsAreNotIgnoredTest--stripOptional0x :: Text -> Text-stripOptional0x h = T.stripPrefix "0x" h ?: h--fromHex :: HasCallStack => Text -> ByteString-fromHex hex =- decodeHex (stripOptional0x hex)- ?: error ("Invalid hex: " <> show hex)---- | Dummy wrapper for the data needed to test an example scenario + its name.-data Example a = Example- { _eTestName :: String- , _eTestData :: a- }---- | Creates a list of example scenarios and labels them using their index.-examples :: [a] -> [Example a]-examples xs =- xs `zip` [1..] <&>- \(testData, index) -> Example ("Example #" <> show @String @Int index) testData---- | Dummy wrapper for what do we test - pack or unpack.-data TestMethod t = TestMethod- { _tmName :: String- , _tmApply :: Value t -> Text -> Expectation- }--packTestMethods- :: (PackedValScope t, HasCallStack)- => [TestMethod t]-packTestMethods = one $- TestMethod "Pack" $- \val encodedHex ->- encodeHex (packValue' val) `shouldBe` stripOptional0x encodedHex--unpackTestMethods- :: (UnpackedValScope t, HasCallStack)- => [TestMethod t]-unpackTestMethods = one $- TestMethod "Unpack" $- \val encodedHex ->- runUnpack (fromHex encodedHex) `shouldBe` Right val--allTestMethods- :: (UnpackedValScope t, HasCallStack)- => [TestMethod t]-allTestMethods = packTestMethods <> unpackTestMethods--packSpecManual- :: HasCallStack- => String -> [TestMethod t] -> (x -> Value t) -> [Example (x, Text)] -> Spec-packSpecManual name testMethods toVal' suites =- forM_ @[_] testMethods $ \(TestMethod mname method) ->- describe mname $- describe name $ forM_ suites $ \(Example exampleName (x, hex)) ->- it exampleName $ method (toVal' x) hex--packSpec- :: forall x (t :: T).- ( IsoValue x, ToT x ~ t- , UnpackedValScope t- , HasCallStack- )- => [Example (x, Text)]- -> Spec-packSpec = packSpecManual typeName allTestMethods toVal- where- typeName = show $ typeRep (Proxy @(Value t))--parsePackSpec- :: forall (inp :: T) (out :: T).- (Each '[KnownT] [inp, out], HasCallStack)- => String- -> [(Text, Text)]- -> Spec-parsePackSpec name suites = parseVLamSpec @inp @out name allTestMethods suites--parseUnpackOnlySpec- :: forall (inp :: T) (out :: T).- (Each '[KnownT] [inp, out], HasCallStack)- => String- -> [(Text, Text)]- -> Spec-parseUnpackOnlySpec name suites = parseVLamSpec @inp @out name unpackTestMethods suites--parseVLamSpec- :: forall (inp :: T) (out :: T).- (Each '[KnownT] [inp, out], HasCallStack)- => String- -> [TestMethod ('TLambda inp out)]- -> [(Text, Text)]- -> Spec-parseVLamSpec name testMethods suites =- forM_ @[_] testMethods $ \(TestMethod mname method) ->- describe mname $- describe name $ forM_ suites $ \(codeText, packed) ->- it (truncateName $ toString codeText) $ do- parsed <- Parser.codeEntry `shouldParse` ("{" <> codeText <> "}")- let code = expandList parsed- let _ :/ typed = typeCheckList code initStack- & runExceptT- & evaluatingState initTypeCheckST- & leftToShowPanic-- case typed of- AnyOutInstr instr ->- method (VLam @inp @out $ RfAlwaysFails instr) packed-- (instr :: Instr '[inp] outs) ::: _ ->- case eqT @'[out] @outs of- Just Refl ->- method (VLam @inp @out $ RfNormal instr) packed- Nothing ->- error "Output type unexpectedly mismatched"- where- truncateName s- | length s < 60 = s- | otherwise = take 60 s <> " ..."- initTypeCheckST = error "Type check state is not defined"- initStack = case getWTP @inp of- Just Dict -> (starNotes @inp, Dict, noAnn) ::& SNil- Nothing -> error "Type is not well typed"--unpackNegSpec- :: forall (t :: T).- (UnpackedValScope t)- => String -> Text -> Spec-unpackNegSpec name encodedHex =- it name $- let encoded = decodeHex (stripOptional0x encodedHex)- ?: error ("Invalid hex: " <> show encodedHex)- in runUnpack @t encoded- `shouldSatisfy` isLeft---- | Helper for defining tests cases for 'packSpec'.--- Read it as "is packed as".-(~:) :: a -> b -> (a, b)-(~:) = (,)-infixr 0 ~:--intTest :: Spec-intTest =- packSpec- @Integer- [ Example "-64" (-64, "0500c001")- , Example "-63" (-63, "05007f")- , Example "-2" (-2, "050042")- , Example "-1" (-1, "050041")- , Example "0" (0, "050000")- , Example "1" (1, "050001")- , Example "2" (2, "050002")- , Example "63" (63, "05003f")- , Example "64" (64, "05008001")- , Example "65" (65, "05008101")- , Example "-65" (-65, "0500c101")- , Example "127" (127, "0500bf01")- , Example "128" (128, "05008002")- , Example "129" (129, "05008102")- , Example "-127" (-127, "0500ff01")- , Example "191" (191, "0500bf02")- , Example "192" (192, "05008003")- , Example "193" (193, "05008103")- , Example "2028" (2028, "0500ac1f")- , Example "5000" (5000, "0500884e")- , Example "10000" (10000, "0500909c01")- , Example "20000" (20000, "0500a0b802")- , Example "-5000" (-5000, "0500c84e")- , Example "-10000" (-10000, "0500d09c01")- , Example "-20000" (-20000, "0500e0b802")- ]--natTest :: Spec-natTest =- packSpec- @Natural- [ Example "0" (0, "050000")- , Example "1" (1, "050001")- , Example "63" (63, "05003f")- , Example "64" (64, "05008001")- , Example "65" (65, "05008101")- , Example "127" (127, "0500bf01")- , Example "128" (128, "05008002")- , Example "129" (129, "05008102")- , Example "191" (191, "0500bf02")- , Example "192" (192, "05008003")- , Example "193" (193, "05008103")- ]--stringTest :: Spec-stringTest =- packSpec- @MText $- [ Example "Hello World!" $ [mt|Hello World!|] ~: "05010000000c48656c6c6f20576f726c6421"- , Example "HODL: Hold On for Dear Life" $- [mt|HODL: Hold On for Dear Life|]- ~: "05010000001b484f444c3a20486f6c64204f6e20666f722044656172204c696665"- , Example "Newline" $- [mt|\n|]- ~: "0501000000010a"- ]--bytesTest :: Spec-bytesTest =- packSpec- @ByteString- [ Example "Numerical" $ "000123" ~: "050a00000006303030313233"- , Example "Alphabetical" $- "A rose by any other name would smell as sweet"- ~: "050a0000002d4120726f736520627920616e79206f74686572206e616\- \d6520776f756c6420736d656c6c206173207377656574"- ]--mutezTest :: Spec-mutezTest =- packSpec- @Mutez- [ Example "0" (unsafeMkMutez 0 , "050000")- , Example "1" (unsafeMkMutez 1 , "050001")- , Example "63" (unsafeMkMutez 63 , "05003f")- , Example "64" (unsafeMkMutez 64 , "05008001")- , Example "65" (unsafeMkMutez 65 , "05008101")- , Example "127" (unsafeMkMutez 127, "0500bf01")- , Example "128" (unsafeMkMutez 128, "05008002")- , Example "129" (unsafeMkMutez 129, "05008102")- , Example "191" (unsafeMkMutez 191, "0500bf02")- , Example "192" (unsafeMkMutez 192, "05008003")- , Example "193" (unsafeMkMutez 193, "05008103")- ]--boolTest :: Spec-boolTest =- packSpec- @Bool- [ Example "True" $ True ~: "05030a"- , Example "False" $ False ~: "050303"- ]--keyHashTest :: Spec-keyHashTest = do- packSpec- @KeyHash- [ Example "KeyHash" $- leftToShowPanic (parseKeyHash "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx")- ~: "050a000000150002298c03ed7d454a101eb7022bc95f7e5f41ac78"- ]--timestampTest :: Spec-timestampTest = do- packSpec @Timestamp $ examples $ convertTimestamps- [ (205027200, "050080dec3c301")- , (1552564995, "0500838cd2c80b")- ]- where- convertTimestamps = map . first $ timestampFromSeconds--addressTest :: Spec-addressTest = do- packSpec @Address $ examples $ parseAddrs- [ ( "tz1PYgf9fBGLXvwx8ag8sdwjLJzmyGdNiswM"- , "050a0000001600002addb327dbca405f07aeef318bba0ec2f714a755"- )- , ( "tz1Z1nn9Y7vzyvtf6rAYMPhPNGqMJXw88xGH"- , "050a00000016000092b72c0fa1064331a641131f572e7f2abb9a890b"- )- , ( "tz2EfqCbLmpfv7mNiLcMmhxAwdgHtPTcwR4W"- , "050a00000016000145b5e7d31bf6612e61ebfa7a6d929ce7800a55a4"- )- , ( "tz3UoffC7FG7zfpmvmjUmUeAaHvzdcUvAj6r"- , "050a0000001600025cfa532f50de3e12befc0ad21603835dd7698d35"- )- , ( "KT1AEseqMV6fk2vtvQCVyA7ZCaxv7cpxtXdB"- , "050a0000001601122d038abd69be91b4b6803f2f098a088e259e7200"- )- , ( "KT1NSrmSJrSueZiWPKrcAUScYr6k2BkUVALr"- , "050a00000016019812c669d9e8ff1a61bf8c57e33b955f074d832600"- )- ]- where- parseAddrs = map $ first unsafeParseAddress--keyTest :: Spec-keyTest =- packSpecManual "key" allTestMethods VKey $ examples- [ item "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"- "050a00000021009a85e0f3f47852869ae667adc3b03a20fa9f324d046174dff6834e7d1fab0e8d"- , item "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"- "050a0000002100aad3f16293766169f7db278c5e0e9db4fb82ffe1cbcc35258059617dc0fec082"- , item "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"- "050a000000220103b524d0184276467c848ac13557fb0ff8bec5907960f72683f22af430503edfc1"- , item "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"- "050a0000002201022c380cd1ff286a0a1a7c3aad6e891d237fa82e2a7cdeec08ccb55e90fdef995f"- , item "p2pk67K1dwkDFPB63RZU5H3SoMCvmJdKZDZszc7U4FiGKN2YypKdDCB"- "050a00000022020368afbb09255d849813712108a4144237dc1fdd5bb74e68335f4c68c12c1e5723"- , item "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"- "050a000000220203dcb1916c475902f2b1083212e1b4e6f8ce1531710218c7d34340439f47040e7c"- ]- where- item keyStr keyBytes = ( leftToShowPanic $ parsePublicKey keyStr, keyBytes)--unitTest :: Spec-unitTest =- packSpec- @()- [Example "Unit" $ () ~: "05030b"]--signatureTest :: Spec-signatureTest =- packSpecManual "signature" allTestMethods VSignature $ examples- [ item "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8\- \ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"- "050a0000004091ac1e7fd668854fc7a40feec4034e42c06c068cce10622c607fda232d\- \b34c8cf5d8da83098dd891cd4cb4299b3fa0352ae323ad99b24541e54b91888fdc8201"- , item "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3CkfbUZyHLLrBxHSmTq\- \rUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"- "0x050a0000004080e4e72ffecf72953789625b1125e9f45f432c14e53a01ec68a1e1b77\- \d60cfe96a97443733ba0f7f42db3a56d7a433df2b4fc0035c05ab92d062f33c5bab0244"- , item "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRs\- \cDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"- "0x050a00000040222222222222222222222222222222222222222222222222222222222\- \22222222222222222222222222222222222222222222222222222222222222222222222"- , item "sigrZRt6CTBNtRzjMFQYSZhUm1QcDg5gopVgiRTLMQsgikeR\- \LmrmsA5vmFqjrnBhofzqvKtc9k5VhTzCMCio5epRvu9no73S"- "0x050a00000040da632d7f267673fab5a40562778a6890b6ada9665d53d7ff318e3399e\- \032b3986588dadcf3bf3b549592f7b8ea1365273fbef5f4883c3430ed32e8ae24017be1"- ]- where- item sigStr sigBytes = (leftToShowPanic $ parseSignature sigStr, sigBytes)--chainIdTest :: Spec-chainIdTest =- packSpecManual "chain_id" allTestMethods VChainId $ examples- [ ( leftToShowPanic $- parseChainId "NetXUdfLh6Gm88t"- , "0x050a00000004458aa837"- )- ]--optionTest :: Spec-optionTest = do- packSpec- @(Maybe Integer)- [ Example "Just" $ Just 123 ~: "05050900bb01"- , Example "Nothing" $ Nothing ~: "050306"- ]- packSpec- @(Maybe MText)- [ Example "Just" $- (Just [mt|Goodnight World!|])- ~: "0505090100000010476f6f646e6967687420576f726c6421"- ]--listTest :: Spec-listTest =- packSpec- @[Integer]- [ Example "Empty list" $ [] ~: "050200000000"- , Example "Singleton list" $ [1] ~: "0502000000020001"- , Example "List with many elements" $ [1..3] ~: "050200000006000100020003"- ]--setTest :: Spec-setTest =- packSpec- @(Set Integer)- [ Example "Empty list" $ [] ~: "050200000000"- , Example "Singleton list" $ [1] ~: "0502000000020001"- , Example "List with many elements" $ [0, 10, 24, 35, 100, 1000] ~: "05020000000e0000000a0018002300a40100a80f"- ]--contractTest :: Spec-contractTest = do- packSpecManual "simple contract" packTestMethods (addressToVContract @'TUnit) $ examples $ parseAddrs $- [ "tz1PYgf9fBGLXvwx8ag8sdwjLJzmyGdNiswM"- ~: "050a0000001600002addb327dbca405f07aeef318bba0ec2f714a755"- , "tz1Z1nn9Y7vzyvtf6rAYMPhPNGqMJXw88xGH"- ~: "050a00000016000092b72c0fa1064331a641131f572e7f2abb9a890b"- ]-- packSpecManual "non-unit contract" packTestMethods (addressToVContract @'TInt) $ examples $ parseAddrs $- [ "KT1AEseqMV6fk2vtvQCVyA7ZCaxv7cpxtXdB"- ~: "0x050a0000001601122d038abd69be91b4b6803f2f098a088e259e7200"- ]-- packSpecManual "contract with entrypoint" packTestMethods (mkEpVContract @'TInt) $ examples $ parseEpAddrs- [ "KT1AEseqMV6fk2vtvQCVyA7ZCaxv7cpxtXdB%"- ~: "0x050a0000001601122d038abd69be91b4b6803f2f098a088e259e7200"- , "KT1AEseqMV6fk2vtvQCVyA7ZCaxv7cpxtXdB%abacaba"- ~: "0x050a0000001d01122d038abd69be91b4b6803f2f098a088e259e720061626163616261"- , "tz1Z1nn9Y7vzyvtf6rAYMPhPNGqMJXw88xGH%a"- ~: "0x050a00000017000092b72c0fa1064331a641131f572e7f2abb9a890b61"- ]- where- parseAddrs = map $ first unsafeParseAddress- parseEpAddrs = map $ first unsafeParseEpAddress-- mkEpVContract :: forall p. ParameterScope p => EpAddress -> Value ('TContract p)- mkEpVContract EpAddress{..} =- VContract eaAddress (SomeEpc epcCallRootUnsafe{ epcName = eaEntryPoint })--pairTest :: Spec-pairTest = do- packSpec- @(MText, Integer) $- examples- [ ([mt|Good Night!|], 5) ~: "050707010000000b476f6f64204e69676874210005"- ]- packSpec- @(Integer, (Integer, MText)) $- examples- [ (120, (5, [mt|What is that?|]))- ~: "05070700b80107070005010000000d5768617420697320746861743f"- ]--orTest :: Spec-orTest =- packSpec- @(Either MText Bool)- [ Example "Left" $ Left [mt|Error|] ~: "05050501000000054572726f72"- , Example "Right" $ Right True ~: "050508030a"- ]--mapTest :: Spec-mapTest = do- packSpec- @(Map Integer MText)- [ Example "Empty map" $ [] ~: "050200000000"- , Example "Non-empty map" $ [(0, [mt|Hello|]), (1, [mt|Goodbye|]), (2, [mt|Goodnight|])]- ~: "05020000003007040000010000000548656c6c6f07040001010000000\- \7476f6f64627965070400020100000009476f6f646e69676874"- ]-- packSpec- @(Map MText (Integer, Bool)) $- examples- [ [ ([mt|Tudor|], (123, True))- , ([mt|Lancaster|], (22323, False))- , ([mt|Stuart|], (-832988, True))- ] ~: "050200000040070401000000094c616e636173746572070700b3dc0203\- \0307040100000006537475617274070700dcd765030a07040100000005\- \5475646f72070700bb01030a"- ]--instrTest :: Spec-instrTest = do- -- Values we compare against are produced with command- -- ./alphanet.sh client hash data "{ $instrs }" of type 'lambda int int'-- parsePackSpec @'TInt @'TInt "instr"- [ ""- ~: "0x050200000000"- , "PUSH int 1; DROP"- ~: "0x0502000000080743035b00010320"- , "DUP; SWAP; DROP"- ~: "0x0502000000060321034c0320"- , "UNIT; DUUP; DROP 2"- ~: "0x050200000016034f020000000b051f02000000020321034c05200002"- , "UNIT; UNIT; DUUUP; DROP 3"- ~: "0x05020000001c034f034f020000000f071f0002020000000203210570000305200003"- , "DIG 0"- ~: "0x05020000000405700000"- , "UNIT; DIG 1; DIP { DROP }"- ~: "0x05020000000f034f05700001051f02000000020320"- , "DUG 0"- ~: "0x05020000000405710000"- , "UNIT; DUG 1; DIP { DROP }"- ~: "0x05020000000f034f05710001051f02000000020320"- , "UNIT; DROP"- ~: "0x050200000004034f0320"- , "UNIT :u; DROP"- ~: "0x05020000000a044f000000023a750320"- , "UNIT; DROP 1"- ~: "0x050200000006034f05200001"- , "DROP 0"- ~: "0x05020000000405200000"- , "UNIT; UNIT; UNIT; DROP 3"- ~: "0x05020000000a034f034f034f05200003"- , "PUSH int 1; SOME; IF_NONE {} {DROP}"- ~: "0x0502000000160743035b00010346072f020000000002000000020320"- , "PUSH int 1; SOME; IF_SOME {DROP} {}"- ~: "0x05020000001b0743035b00010346020000000e072f020000000002000000020320"- , "PUSH int 1; SOME :s; IF_SOME {DROP} {}"- ~: "0x0502000000210743035b00010446000000023a73020000000e072f020000000\- \002000000020320"- , "NONE int; DROP"- ~: "0x050200000006053e035b0320"- , "NONE :n int; DROP"- ~: "0x05020000000c063e035b000000023a6e0320"- , "PUSH int 1; PAIR; CAR"- ~: "0x05020000000a0743035b000103420316"- , "PUSH int 1; PAIR :p; CAR"- ~: "0x0502000000100743035b00010442000000023a700316"- , "PUSH int 1; PAIR % %o; CAR"- ~: "0x0502000000120743035b00010442000000042520256f0316"- , "PUSH int 1; PAIR %o %; CAR"- ~: "0x0502000000100743035b0001044200000002256f0316"- , "PUSH int 1; PAIR %o; CAR"- ~: "0x0502000000100743035b0001044200000002256f0316"- , "PUSH int 1; PAIR :p %l %r; CAR"- ~: "0x0502000000160743035b00010442000000083a7020256c2025720316"- , "PUSH int 1; PAIR %l %r; CAR %l"- ~: "0x0502000000190743035b0001044200000005256c202572041600000002256c"- , "PUSH int 1; PAIR %l %r; CDR %r"- ~: "0x0502000000190743035b0001044200000005256c2025720417000000022572"- , "LEFT unit; IF_LEFT {} { DROP; PUSH int 1 }"- ~: "0x0502000000180533036c072e0200000000020000000803200743035b0001"- , "LEFT :lu %l %r unit; IF_LEFT {} { DROP; PUSH int 1 }"- ~: "0x0502000000250633036c000000093a6c7520256c202572072e0200000000020\- \000000803200743035b0001"- , "RIGHT unit; IF_RIGHT {} { DROP; PUSH int 1 }"- ~: "0x05020000001d0544036c0200000014072e020000000803200743035b00010200000000"- , "DUP; NIL int; SWAP; CONS; SIZE; DROP"- ~: "0x05020000000e0321053d035b034c031b03450320"- , "NIL int; IF_CONS { DROP; DROP } {}"- ~: "0x050200000014053d035b072d0200000004032003200200000000"- , "NIL :ni int; IF_CONS { DROP; DROP } {}"- ~: "0x05020000001b063d035b000000033a6e69072d0200000004032003200200000000"- , "EMPTY_SET int; ITER { DROP }"- ~: "0x05020000000d0524035b055202000000020320"- , "EMPTY_SET :si int; ITER { DROP }"- ~: "0x0502000000140624035b000000033a7369055202000000020320"- , "EMPTY_MAP int unit; MAP {}; DROP"- ~: "0x05020000000f0723035b036c053802000000000320"- , "EMPTY_MAP :miu int unit; MAP {}; DROP"- ~: "0x0502000000170823035b036c000000043a6d6975053802000000000320"- , "EMPTY_MAP int unit; PUSH int 1; MEM; DROP"- ~: "0x0502000000100723035b036c0743035b000103390320"- , "EMPTY_MAP int unit; PUSH int 1; GET; DROP"- ~: "0x0502000000100723035b036c0743035b000103290320"- , "EMPTY_MAP int unit; NONE unit; PUSH int 1; UPDATE; DROP"- ~: "0x0502000000140723035b036c053e036c0743035b000103500320"- , "EMPTY_BIG_MAP int unit; PUSH int 1; GET; DROP"- ~: "0x0502000000100772035b036c0743035b000103290320"- , "EMPTY_BIG_MAP :bmiu int unit; PUSH int 1; GET; DROP"- ~: "0x0502000000190872035b036c000000053a626d69750743035b000103290320"- , "PUSH bool True; IF {} {}"- ~: "0x05020000001207430359030a072c02000000000200000000"- , "PUSH bool True; LOOP { PUSH bool False }"- ~: "0x05020000001307430359030a05340200000006074303590303"- , "PUSH (or int int) (Left 1); LOOP_LEFT { RIGHT int }; DROP"- ~: "0x05020000001907430764035b035b05050001055302000000040544035b0320"- , "LAMBDA int int { PUSH int 1; DROP }; SWAP; EXEC"- ~: "0x05020000001f093100000011035b035b02000000080743035b0001032000000000034c0326"- , "DUP; LAMBDA (pair int int) int { CAR }; SWAP; APPLY; SWAP; EXEC"- ~: "0x050200000023032109310000000f0765035b035b035b0200000002031600000000034c0373\- \034c0326"- , "DIP {}"- ~: "0x050200000007051f0200000000"- , "DIP 1 {}"- ~: "0x050200000009071f00010200000000"- , "DIP 0 {}"- ~: "0x050200000009071f00000200000000"- , "UNIT; UNIT; DIP 3 { UNIT; DROP }; DROP; DROP"- ~: "0x050200000015034f034f071f00030200000004034f032003200320"- , "UNIT; UNIT; DIIIP { UNIT; DROP }; DROP; DROP"- ~: "0x050200000015034f034f071f00030200000004034f032003200320"- , "FAILWITH"- ~: "0x0502000000020327"- , "CAST int"- ~: "0x0502000000040557035b"- , "RENAME"- ~: "0x0502000000020358"- , "DUP; PACK; UNPACK unit; DROP"- ~: "0x05020000000a0321030c050d036c0320"- , "DUP; PACK; UNPACK :uu unit; DROP"- ~: "0x0502000000110321030c060d036c000000033a75750320"- , "PUSH string \"\"; DUP; CONCAT; DROP"- ~: "0x05020000000f0743036801000000000321031a0320"- , "NIL string; CONCAT; DROP"- ~: "0x050200000008053d0368031a0320"- , "PUSH string \"\"; PUSH nat 1; PUSH nat 2; SLICE; DROP"- ~: "0x050200000019074303680100000000074303620001074303620002036f0320"- , "PUSH int 1; ISNAT; DROP"- ~: "0x05020000000a0743035b000103560320"- -- Arithmetic instructions are below- , "PUSH nat 1; INT; DROP"- ~: "0x05020000000a07430362000103300320"- -- SELF cannot appear in lambda- -- CONTRACT - IMPLICIT_ACCOUNT go below- , "NOW; DROP"- ~: "0x05020000000403400320"- , "AMOUNT; DROP"- ~: "0x05020000000403130320"- , "BALANCE; DROP"- ~: "0x05020000000403150320"- -- CHECK_SIGNATURE goes below- , "PUSH bytes 0x; SHA256; DROP"- ~: "0x05020000000d074303690a00000000030f0320"- , "PUSH bytes 0x; SHA512; DROP"- ~: "0x05020000000d074303690a0000000003100320"- , "PUSH bytes 0x; BLAKE2B; DROP"- ~: "0x05020000000d074303690a00000000030e0320"- -- HASH_KEY goes below- , "SOURCE; DROP"- ~: "0x05020000000403470320"- , "SENDER; DROP"- ~: "0x05020000000403480320"- -- 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"- [ "PUSH int 1; PUSH int 2; ADD; DROP"- ~: "0x0502000000100743035b00010743035b000203120320"- , "PUSH int 1; PUSH int 2; SUB; DROP"- ~: "0x0502000000100743035b00010743035b0002034b0320"- , "PUSH int 1; PUSH int 2; MUL; DROP"- ~: "0x0502000000100743035b00010743035b0002033a0320"- , "PUSH int 1; PUSH int 2; EDIV; DROP"- ~: "0x0502000000100743035b00010743035b000203220320"- , "PUSH int 1; ABS; DROP"- ~: "0x05020000000a0743035b000103110320"- , "PUSH int 1; NEG @kek; DROP"- ~: "0x05020000000a0743035b0001033b0320"- , "PUSH nat 1; PUSH nat 2; LSL; DROP"- ~: "0x05020000001007430362000107430362000203350320"- , "PUSH nat 1; PUSH nat 2; LSR; DROP"- ~: "0x05020000001007430362000107430362000203360320"- , "PUSH nat 1; PUSH nat 2; OR; DROP"- ~: "0x05020000001007430362000107430362000203410320"- , "PUSH nat 1; PUSH nat 2; XOR; DROP"- ~: "0x05020000001007430362000107430362000203510320"- , "PUSH int 1; NOT; DROP"- ~: "0x05020000000a0743035b0001033f0320"- , "PUSH nat 1; PUSH nat 2; COMPARE; DROP"- ~: "0x05020000001007430362000107430362000203190320"- , "PUSH int 1; EQ; DROP"- ~: "0x05020000000a0743035b000103250320"- , "PUSH int 1; NEQ; DROP"- ~: "0x05020000000a0743035b0001033c0320"- , "PUSH int 1; LT; DROP"- ~: "0x05020000000a0743035b000103370320"- , "PUSH int 1; GT; DROP"- ~: "0x05020000000a0743035b0001032a0320"- , "PUSH int 1; LE; DROP"- ~: "0x05020000000a0743035b000103320320"- , "PUSH int 1; GE; DROP"- ~: "0x05020000000a0743035b000103280320"- ]-- parsePackSpec @'TAddress @'TUnit "instrs address-related"- [ "CONTRACT unit; DROP; PUSH unit Unit"- ~: "0x05020000000c0555036c03200743036c030b"- , "CONTRACT %entrypnt unit; DROP; PUSH unit Unit"- ~: "0x0502000000190655036c0000000925656e747279706e7403200743036c030b"- ]-- parsePackSpec @('TContract 'TUnit) @'TUnit "instrs contract-related"- [ "PUSH mutez 5; PUSH unit Unit; TRANSFER_TOKENS; DROP; PUSH unit Unit"- ~: "0x0502000000160743036a00050743036c030b034d03200743036c030b"- , "ADDRESS; DROP; PUSH unit Unit"- ~: "0x05020000000a035403200743036c030b"- ]-- parsePackSpec @'TKeyHash @'TUnit "instrs key-hash-related"- [ "SOME; SET_DELEGATE; DROP; PUSH unit Unit"- ~: "0x05020000000c0346034e03200743036c030b"- , "SOME; DIP{ PUSH unit Unit; PUSH mutez 5; }; \- \ CREATE_CONTRACT{ \- \ parameter unit; \- \ storage unit; \- \ code { DROP; UNIT; NIL operation; PAIR } \- \ }; \- \ DROP; DROP; PUSH unit Unit \- \"- ~: "0x05020000003f0346051f020000000c0743036c030b0743036a0005051d020000\- \00190500036c0501036c0502020000000a0320034f053d036d0342032003200743\- \036c030b"- , "SOME; DIP{ PUSH int 1; PUSH mutez 5 }; \- \ CREATE_CONTRACT{ \- \ parameter (int :p); \- \ storage (int :s); \- \ code { DROP; PUSH int 10; NIL operation; PAIR } \- \ }; \- \ DROP; DROP; PUSH unit Unit \- \"- ~: "0x05020000004f0346051f020000000c0743035b00010743036a0005051d020000\- \00290500045b000000023a700501045b000000023a730502020000000e03200743\- \035b000a053d036d0342032003200743036c030b"- , "SOME; DIP{ PUSH int 1; PUSH mutez 5 }; \- \ CREATE_CONTRACT{ \- \ parameter (int %root :p); \- \ storage (int :s); \- \ code { DROP; PUSH int 10; NIL operation; PAIR } \- \ }; \- \ DROP; DROP; PUSH unit Unit \- \"- ~: "0502000000550346051f020000000c0743035b00010743036a0005051d020000002\- \f0500045b000000083a702025726f6f740501045b000000023a730502020000000e\- \03200743035b000a053d036d0342032003200743036c030b"- , "IMPLICIT_ACCOUNT; DROP; PUSH unit Unit"- ~: "0x05020000000a031e03200743036c030b"- ]-- parsePackSpec @'TKey @'TKeyHash "instrs public-key-related"- [ "HASH_KEY"- ~: "0x050200000002032b"- ]-- parsePackSpec @('TPair 'TSignature 'TKey) @'TBool "instrs public-key-related"- [ "DIP{ PUSH bytes 0x }; DUP; DIP {CAR}; CDR; CHECK_SIGNATURE"- ~: "0x05020000001f051f0200000009074303690a000000000321051f020000\- \0002031603170318"- ]--typesTest :: Spec-typesTest = do- -- Bytes we compare agains are produced with command- -- ./alphanet.sh client hash data '{ LAMBDA ($ty) ($ty) {}; DROP }' of type 'lambda unit unit' /- -- | tr -d '\n' | awk '{ print $45 }' | sed 's/Hash://'-- parsePackSpec @'TUnit @'TUnit "types"- [ lambdaWrap "int"- ~: "0x050200000015093100000009035b035b0200000000000000000320"- , lambdaWrap "int :i"- ~: "0x050200000021093100000015045b000000023a69045b000000023a690200000\- \000000000000320"- , lambdaWrap "nat"- ~: "0x050200000015093100000009036203620200000000000000000320"- , lambdaWrap "string"- ~: "0x050200000015093100000009036803680200000000000000000320"- , lambdaWrap "bytes"- ~: "0x050200000015093100000009036903690200000000000000000320"- , lambdaWrap "mutez"- ~: "0x050200000015093100000009036a036a0200000000000000000320"- , lambdaWrap "bool"- ~: "0x050200000015093100000009035903590200000000000000000320"- , lambdaWrap "key_hash"- ~: "0x050200000015093100000009035d035d0200000000000000000320"- , lambdaWrap "timestamp"- ~: "0x050200000015093100000009036b036b0200000000000000000320"- , lambdaWrap "address"- ~: "0x050200000015093100000009036e036e0200000000000000000320"- , lambdaWrap "key"- ~: "0x050200000015093100000009035c035c0200000000000000000320"- , lambdaWrap "unit"- ~: "0x050200000015093100000009036c036c0200000000000000000320"- , lambdaWrap "signature"- ~: "0x050200000015093100000009036703670200000000000000000320"- , lambdaWrap "chain_id"- ~: "0x050200000015093100000009037403740200000000000000000320"- , lambdaWrap "option unit"- ~: "0x05020000001909310000000d0563036c0563036c0200000000000000000320"- , lambdaWrap "set int"- ~: "0x05020000001909310000000d0566035b0566035b0200000000000000000320"- , lambdaWrap "set :s int"- ~: "0x0502000000250931000000190666035b000000023a730666035b000000023a7\- \30200000000000000000320"- , lambdaWrap "set :s (int :i)"- ~: "0x0502000000310931000000250666045b000000023a69000000023a730666045\- \b000000023a69000000023a730200000000000000000320"- , lambdaWrap "operation"- ~: "0x050200000015093100000009036d036d0200000000000000000320"- , lambdaWrap "contract unit"- ~: "0x05020000001909310000000d055a036c055a036c0200000000000000000320"- , lambdaWrap "pair unit int"- ~: "0x05020000001d0931000000110765036c035b0765036c035b0200000000000000000320"- , lambdaWrap "pair :point (int %x) (int %y)"- ~: "0x05020000004909310000003d0865045b000000022578045b000000022579000\- \000063a706f696e740865045b000000022578045b000000022579000000063a70\- \6f696e740200000000000000000320"- , lambdaWrap "pair :point3d (pair :point (int %x) (int %y)) (int %z)"- ~: "0x05020000007509310000006908650865045b000000022578045b00000002257\- \9000000063a706f696e74045b00000002257a000000083a706f696e7433640865\- \0865045b000000022578045b000000022579000000063a706f696e74045b00000\- \002257a000000083a706f696e7433640200000000000000000320"- , lambdaWrap "or unit int"- ~: "0x05020000001d0931000000110764036c035b0764036c035b0200000000000000000320"- , lambdaWrap "or (unit :u %l) (int :i %r)"- ~: "0x0502000000410931000000350764046c000000053a7520256c045b000000053\- \a692025720764046c000000053a7520256c045b000000053a6920257202000000\- \00000000000320"- , lambdaWrap "lambda unit int"- ~: "0x05020000001d093100000011075e036c035b075e036c035b0200000000000000000320"- , lambdaWrap "lambda :l (unit :n) (int :t)"- ~: "0x050200000041093100000035085e046c000000023a6e045b000000023a74000\- \000023a6c085e046c000000023a6e045b000000023a74000000023a6c02000000\- \00000000000320"- , lambdaWrap "map int unit"- ~: "0x05020000001d0931000000110760035b036c0760035b036c0200000000000000000320"- , lambdaWrap "big_map int unit"- ~: "0x05020000001d0931000000110761035b036c0761035b036c0200000000000000000320"- ]- where- lambdaWrap ty = "LAMBDA " <> ty <> " " <> ty <> " {}; DROP"--unpackNegTest :: Spec-unpackNegTest = do- describe "Bad entries order" $ do- unpackNegSpec @('TSet 'TInt) "Unordered set elements"- "0x050200000006000300020001" -- { 3; 2; 1 }- unpackNegSpec @('TMap 'TInt $ 'TInt) "Unordered map elements"- "0x05020000000c070400020006070400010007" -- { Elt 2 6; Elt 1 7 }-- describe "Wrong length specified" $ do- unpackNegSpec @('TList $ 'TInt) "Too few list length"- "0x05020000000300010002" -- { 1; 2 }- unpackNegSpec @('TList $ 'TInt) "Too big list length"- "0x05020000000500010002" -- { 1; 2 }- unpackNegSpec @('TList $ 'TInt) "Wrong bytes length"- "0x050b000000021234" -- 0x1234-- describe "Type check failures" $ do- unpackNegSpec @('TUnit) "Value type mismatch"- "0x050008" -- 8- unpackNegSpec @('TLambda 'TUnit 'TKey) "Lambda type mismatch"- "0x050200000000" -- {}- unpackNegSpec @('TLambda 'TUnit 'TKey) "Lambda too large output stack size"- "0x0502000000060743035b0005" -- {PUSH int 5}- unpackNegSpec @('TLambda 'TUnit 'TKey) "Lambda empty output stack size"- "0x0502000000020320" -- {DROP}-- describe "Invalid annotations specified" $ do- unpackNegSpec @('TLambda ('TPair 'TInt 'TInt) 'TInt)- "Space char `20` in the middle of an annotation"- "0x050200000009041600000003252061"- unpackNegSpec @('TLambda ('TPair 'TInt 'TInt) 'TInt)- "Char from invalid range `00-1F` as part of an annotation"- "0x050200000009041600000003251A61"- unpackNegSpec @('TLambda ('TPair 'TInt 'TInt) 'TInt)- "Char from invalid range `A0-FF` as part of an annotation"- "0x05020000000904160000000325A361"--unpackVarAnnTest :: Spec-unpackVarAnnTest = do- describe "Unpack instructions with Variable Annotations" $ do- parseUnpackOnlySpec @'TInt @'TInt "VarAnn instr"- [ "DUP @dp; SWAP; DROP"- ~: "0x05020000000d042100000003406470034c0320"- , "UNIT @un; DROP"- ~: "0x05020000000b044f0000000340756e0320"- , "NONE @nn int; DROP"- ~: "0x05020000000d063e035b00000003406e6e0320"- , "PUSH @vn int 1; PAIR @vpn; CAR @vn"- ~: "0x0502000000200843035b00010000000340766e0442000000044076706e0416\- \0000000340766e"- , "PUSH int 1; PAIR; CDR @dr"- ~: "0x0502000000110743035b00010342041700000003406472"- , "LEFT @ll unit; IF_LEFT {} { DROP; PUSH int 1 }"- ~: "0x05020000001f0633036c00000003406c6c072e020000000002000000080320\- \0743035b0001"- , "RIGHT @rl unit; IF_RIGHT {} { DROP; PUSH int 1 }"- ~: "0x0502000000240644036c0000000340726c0200000014072e02000000080320\- \0743035b00010200000000"- , "DUP; NIL @al int; SWAP; CONS @dl; SIZE @sl; DROP"- ~: "0x0502000000230321063d035b0000000340616c034c041b0000000340646c04\- \450000000340736c0320"- , "EMPTY_SET :si @si int; ITER { DROP }"- ~: "0x0502000000180624035b000000073a736920407369055202000000020320"- , "EMPTY_MAP @em :miu int unit; MAP @dm {}; DROP"- ~: "0x0502000000220823035b036c0000000840656d203a6d697506380200000000\- \0000000340646d0320"- , "EMPTY_MAP @sm int unit; PUSH int 1; MEM @mmm; DROP"- ~: "0x05020000001f0823035b036c0000000340736d0743035b0001043900000004406d6d6d0320"- , "EMPTY_MAP int unit; NONE unit; PUSH int 1; UPDATE @ups; DROP"- ~: "0x05020000001c0723035b036c053e036c0743035b0001045000000004407570730320"- , "EMPTY_BIG_MAP int unit; PUSH int 1; GET @gg; DROP"- ~: "0x0502000000170772035b036c0743035b00010429000000034067670320"- , "LAMBDA @lii int int { PUSH int 1; DROP }; SWAP; EXEC"- ~: "0x050200000023093100000011035b035b02000000080743035b000103200000\- \0004406c6969034c0326"- , "PUSH string \"\"; DUP; CONCAT @c; DROP"- ~: "0x0502000000150743036801000000000321041a0000000240630320"- , "PUSH int 1; PUSH int 2; ADD @a; DROP"- ~: "0x0502000000160743035b00010743035b000204120000000240610320"- , "PUSH int 1; PUSH int 2; SUB @s; DROP"- ~: "0x0502000000160743035b00010743035b0002044b0000000240730320"- , "PUSH int 1; PUSH int 2; MUL @m; DROP"- ~: "0x0502000000160743035b00010743035b0002043a00000002406d0320"- , "PUSH int 1; PUSH int 2; EDIV @edv; DROP"- ~: "0x0502000000180743035b00010743035b0002042200000004406564760320"- , "PUSH int 1; ABS @a; DROP"- ~: "0x0502000000100743035b000104110000000240610320"- , "PUSH int 1; NEG @n; DROP"- ~: "0x0502000000100743035b0001043b00000002406e0320"- , "PUSH nat 1; PUSH nat 2; LSL @ll; DROP"- ~: "0x050200000017074303620001074303620002043500000003406c6c0320"- , "PUSH nat 1; PUSH nat 2; LSR @lr; DROP"- ~: "0x050200000017074303620001074303620002043600000003406c720320"- , "PUSH nat 1; PUSH nat 2; OR @o; DROP"- ~: "0x050200000016074303620001074303620002044100000002406f0320"- , "PUSH nat 1; PUSH nat 2; XOR @x; DROP"- ~: "0x05020000001607430362000107430362000204510000000240780320"- , "PUSH int 1; NOT @n; DROP"- ~: "0x0502000000100743035b0001043f00000002406e0320"- , "PUSH nat 1; PUSH nat 2; COMPARE @cp; DROP"- ~: "0x0502000000170743036200010743036200020419000000034063700320"- , "PUSH int 1; EQ @e; DROP"- ~: "0x0502000000100743035b000104250000000240650320"- , "PUSH int 1; NEQ @ne; DROP"- ~: "0x0502000000110743035b0001043c00000003406e650320"- , "PUSH int 1; LT @l; DROP"- ~: "0x0502000000100743035b0001043700000002406c0320"- , "PUSH int 1; GT @g; DROP"- ~: "0x0502000000100743035b0001042a0000000240670320"- , "PUSH int 1; LE @e; DROP"- ~: "0x0502000000100743035b000104320000000240650320"- , "PUSH int 1; GE @g; DROP"- ~: "0x0502000000100743035b000104280000000240670320"- , "PUSH int 1; ISNAT @i; DROP"- ~: "0x0502000000100743035b000104560000000240690320"- , "PUSH nat 1; INT @i; DROP"- ~: "0x05020000001007430362000104300000000240690320"- , "NOW @n; DROP"- ~: "0x05020000000a044000000002406e0320"- , "AMOUNT @a; DROP"- ~: "0x05020000000a04130000000240610320"- , "BALANCE @b; DROP"- ~: "0x05020000000a04150000000240620320"- , "PUSH bytes 0x; BLAKE2B @b2b; DROP"- ~: "0x050200000015074303690a00000000040e00000004406232620320"- , "SOURCE @src; DROP"- ~: "0x05020000000c044700000004407372630320"- , "SENDER @s; DROP"- ~: "0x05020000000a04480000000240730320"- , "CHAIN_ID @cid; DROP"- ~: "0x05020000000c047500000004406369640320"- , "CAST @c int"- ~: "0x05020000000a0657035b000000024063"- , "RENAME @r"- ~: "0x0502000000080458000000024072"- ]-- parseUnpackOnlySpec @'TAddress @'TUnit "VarAnn instrs address-related"- [ "CONTRACT @c unit; DROP; PUSH unit Unit"- ~: "0x0502000000120655036c00000002406303200743036c030b"- ]-- parseUnpackOnlySpec @('TContract 'TUnit) @'TUnit "VarAnn instrs contract-related"- [ "ADDRESS @a; DROP; PUSH unit Unit"- ~: "0x050200000010045400000002406103200743036c030b"- ]-- parseUnpackOnlySpec @'TKeyHash @'TUnit "VarAnn instrs key-hash-related"- [ "SOME; SET_DELEGATE @d; DROP; PUSH unit Unit"- ~: "0x0502000000120346044e00000002406403200743036c030b"- , "SOME; DIP{ PUSH int 1; PUSH mutez 5 }; \- \ CREATE_CONTRACT @ez { \- \ parameter (int :p); \- \ storage (int :s); \- \ code { DROP; PUSH int 10; NIL operation; PAIR } \- \ }; \- \ DROP; DROP; PUSH unit Unit \- \"- ~: "0x0502000000560346051f020000000c0743035b00010743036a0005061d02000\- \000290500045b000000023a700501045b000000023a730502020000000e032007\- \43035b000a053d036d03420000000340657a032003200743036c030b"- , "SOME; DIP{ PUSH int 1; PUSH mutez 5 }; \- \ CREATE_CONTRACT @ez @pz { \- \ parameter (int :p); \- \ storage (int :s); \- \ code { DROP; PUSH int 10; NIL operation; PAIR } \- \ }; \- \ DROP; DROP; PUSH unit Unit \- \"- ~: "0x05020000005a0346051f020000000c0743035b00010743036a0005061d02000\- \000290500045b000000023a700501045b000000023a730502020000000e032007\- \43035b000a053d036d03420000000740657a2040707a032003200743036c030b"- , "IMPLICIT_ACCOUNT @i; DROP; PUSH unit Unit"- ~: "0x050200000010041e00000002406903200743036c030b"- ]-- parseUnpackOnlySpec @'TKey @'TKeyHash "VarAnn instrs public-key-related"- [ "HASH_KEY @h"- ~: "0x050200000008042b000000024068"- ]-- parseUnpackOnlySpec @('TPair 'TSignature 'TKey) @'TBool "VarAnn instrs public-key-related"- [ "DIP{ PUSH bytes 0x }; DUP; DIP {CAR}; CDR; CHECK_SIGNATURE @c"- ~: "0x050200000025051f0200000009074303690a000000000321051f0200000002\- \031603170418000000024063"- ]---readableUnpackTest :: Spec-readableUnpackTest = do- describe "Unpack crypto primitives from Readable representation" $ do- -- ./tezos-client hash data '"tz1akcPmG1Kyz2jXpS4RvVJ8uWr7tsiT9i6A"' of type 'string'- unpackReadableSpec @'TAddress "Unpack readable address"- "0x050100000024747a31666173774354446369527a45346f4a396a6e32566\- \d3264766a6579413966557a55"- -- ./tezos-client hash data '"edpktezaD1wnUa5pT2pvj1JGHNey18WGhPc9fk9bbppD33KNQ2vH8R"'- -- of type 'string'- unpackReadableSpec @'TKey "Unpack readable public key"- "0x0501000000366564706b74657a614431776e55613570543270766a314a4\- \7484e65793138574768506339666b39626270704433334b4e513276483852"- -- ./tezos-client-babylonnet hash data '"edsigtqgdc2JLMDcERHo61Y76mrxqCeqDE5YhHiBo\- -- \VtwjhFKahAkCT7RCZKQLhLJ3yJbrVyJCkVGEoiHbyKytHW846dDC4P121K"' of type 'string'- unpackReadableSpec @'TSignature "Unpack readable signature"- "0x05010000006365647369677471676463324a4c4d44634552486f36315937\- \366d72787143657144453559684869426f5674776a68464b6168416b435437\- \52435a4b514c684c4a33794a627256794a436b5647456f694862794b797448\- \5738343664444334503132314b"-- -- ./tezos-client-babylonnet hash data '"NetXUdfLh6Gm88t"' of type string- unpackReadableSpec @'TChainId "Unpack readable chain_id"- "0x05010000000f4e6574585564664c6836476d383874"-- where- unpackReadableSpec- :: forall t. (UnpackedValScope t)- => String -> Text -> Spec- unpackReadableSpec name encodedHex =- it name $- let encoded = decodeHex (stripOptional0x encodedHex)- ?: error ("Invalid hex: " <> show encodedHex)- in runUnpack @t encoded `shouldSatisfy` isRight--lengthsAreNotIgnoredTest :: Spec-lengthsAreNotIgnoredTest =- describe "Lengths are not ignored in ChainId" $ do- let chainId = ChainIdUnsafe "\0\0\0\0"- properEncoded = fromHex "050a0000000400000000"- badEncodedGt = fromHex "050a0000000500000000"- badEncodedLt = fromHex "050a0000000300000000"-- it "Can be properly encoded" $- unpackValue' @'TChainId properEncoded == Right (VChainId chainId)- it "Caught when greater than necessary" $- isLeft $ unpackValue' @'TChainId badEncodedGt- it "Caught when less than necessary" $- isLeft $ unpackValue' @'TChainId badEncodedLt
− test/Test/Tezos/Address.hs
@@ -1,54 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Tezos.Address'.--module Test.Tezos.Address- ( test_Roundtrip- , test_parseAddress- ) where--import Test.Hspec (shouldSatisfy)-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Tezos.Address (formatAddress, genAddress, parseAddress)--import Test.Util.Hedgehog (ShowThroughBuild(..), aesonRoundtrip, roundtripTreeSTB)--test_Roundtrip :: [TestTree]-test_Roundtrip =- [ testGroup "parse . format ≡ pure"- [ roundtripTreeSTB genAddress formatAddress parseAddress ]- , testGroup "JSON encoding/deconding"- [ aesonRoundtrip genAddress ]- ]--test_parseAddress :: [TestTree]-test_parseAddress =- [ testCase "Successfully parses valid sample data" $- forM_ sampleAddresses (\a -> bimap STB STB (parseAddress a) `shouldSatisfy` isRight)- , testCase "Fails to parse invalid data" $ do- forM_ invalidAddresses (\a -> bimap STB STB (parseAddress a) `shouldSatisfy` isLeft)- ]- where- sampleAddresses =- [ "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU"- , "tz1Wit2PqodvPeuRRhdQXmkrtU8e8bRYZecd"- , "tz2EfqCbLmpfv7mNiLcMmhxAwdgHtPTcwR4W"- , "tz2Darj3LyQzekU98ZK8diHvuyn1YYjcHpc6"- , "tz3UoffC7FG7zfpmvmjUmUeAaHvzdcUvAj6r"- , "tz3NExpXn9aPNZPorRE4SdjJ2RGrfbJgMAaV"- , "KT1G4hcQj2STN86GwC1XAkPtwPPhgfPKuE45"- , "KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh"- ]- invalidAddresses =- [ ""- , "1"- -- These are slightly modified versions of some valid addresses above.- , "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUZU"- , "tz2darj3LyQzekU98ZK8diHvuyn1YYjcHpc6"- , "tz3UoffC8FG7zfpmvmjUmUeAaHvzdcUvAj6r"- , "KT1G4hcQj2STN86GwC1XAkPtwPPhgfPKuE46"- ]
− test/Test/Tezos/Core.hs
@@ -1,80 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Tezos.Core- ( test_ParseTimestamp- , test_TimestampQuote- , test_ChainId_roundtrip- , test_Parse_ChainId- ) where--import Test.HUnit ((@?=))-import Test.Tasty (TestTree)-import Test.Tasty.HUnit (testCase)-import Test.Util.Hedgehog (ShowThroughBuild(..), aesonRoundtrip, roundtripTreeSTB)-import Test.Hspec (shouldBe, shouldSatisfy)-import Test.Tasty (testGroup)--import Tezos.Core--------------------------------------------------------------------------------- Timestamp-------------------------------------------------------------------------------test_ParseTimestamp :: [TestTree]-test_ParseTimestamp =- [ testCase "Correctly parse valid inputs" $ do- parseTimestamp "2018-08-08T00:00:00Z" `shouldBe` Just (Timestamp 1533686400)- parseTimestamp "2018-08-08 00:00:00Z" `shouldBe` Just (Timestamp 1533686400)-- , testCase "Does not parse leading and trailing spaces" $ do- parseTimestamp " 2018-08-08T00:00:00Z" `shouldSatisfy` isNothing- parseTimestamp "2018-08-08T00:00:00Z " `shouldSatisfy` isNothing- parseTimestamp " 2018-08-08T00:00:00Z " `shouldSatisfy` isNothing-- , testCase "Does not parse with multiple separating spaces" $ do- parseTimestamp "2018-08-08 00:00:00Z" `shouldSatisfy` isNothing- parseTimestamp "2018-08-08 00:00:00Z" `shouldSatisfy` isNothing- ]--test_TimestampQuote :: [TestTree]-test_TimestampQuote =- [ testCase "Quoter works in simple case" $- [timestampQuote|2019-07-26T12:09:12Z|]- @?= timestampFromSeconds 1564142952-- , testCase "Quoter works with surrounding spaces" $- [timestampQuote| 2019-07-26T12:09:12Z |]- @?= timestampFromSeconds 1564142952- ]--------------------------------------------------------------------------------- ChainId-------------------------------------------------------------------------------test_ChainId_roundtrip :: [TestTree]-test_ChainId_roundtrip =- [ testGroup "parse . format ≡ pure"- [ roundtripTreeSTB genChainId formatChainId parseChainId ]- , testGroup "JSON encoding/deconding"- [ aesonRoundtrip genChainId ]- ]--test_Parse_ChainId :: [TestTree]-test_Parse_ChainId =- [ testCase "Successfully parses valid sample data" $- forM_ sampleChainIds (\a -> bimap STB STB (parseChainId a) `shouldSatisfy` isRight)- , testCase "Fails to parse invalid data" $ do- forM_ invalidChainIds (\a -> bimap STB STB (parseChainId a) `shouldSatisfy` isLeft)- ]- where- sampleChainIds =- [ "NetXUdfLh6Gm88t"- ]- invalidChainIds =- [ ""- , "1"- , "NetXUdfLh6Gm88r" -- last char changed- , "5uYAAtMU9Bi2Qe" -- valid base58, wrong size- ]
− test/Test/Tezos/Crypto.hs
@@ -1,217 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Tezos.Crypto'.--module Test.Tezos.Crypto- ( test_Roundtrip- , test_Signing- , unit_Key_Hashing- ) where--import Fmt (pretty)-import Test.Hspec (Expectation, shouldSatisfy)-import Test.HUnit (Assertion, (@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Tezos.Crypto--import Test.Util.Hedgehog (aesonRoundtrip, roundtripTreeSTB)--test_Roundtrip :: [TestTree]-test_Roundtrip =- [ testGroup "parse . format ≡ pure"- [ roundtripTreeSTB genPublicKey formatPublicKey parsePublicKey- , roundtripTreeSTB genSignature formatSignature parseSignature- , roundtripTreeSTB genKeyHash formatKeyHash parseKeyHash- ]- , testGroup "JSON encoding/deconding"- [ aesonRoundtrip genPublicKey- , aesonRoundtrip genSignature- , aesonRoundtrip genKeyHash- ]- ]--------------------------------------------------------------------------------- Signing-------------------------------------------------------------------------------test_Signing :: [TestTree]-test_Signing =- [ testGroup "Formatting"- [ testGroup "parsePublicKey"- [ testCase "Successfully parses valid sample data" $- mapM_ (parsePublicKeySample . sdPublicKey) sampleSignatures- , testCase "Fails to parse invalid data" $ do- parsePublicKeyInvalid ""- parsePublicKeyInvalid "aaa"-- parsePublicKeyInvalid- "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3v"- parsePublicKeyInvalid- "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"-- parsePublicKeyInvalid- "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBd"- parsePublicKeyInvalid- "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3CkfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"-- parsePublicKeyInvalid- "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYbfpxk"- parsePublicKeyInvalid- "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"- ]- , testGroup "parseSignature"- [ testCase "Successfully parses valid sample data" $- mapM_ (parseSignatureSample . sdSignature) sampleSignatures- , testCase "Fails to parse invalid data" $ do- parseSignatureInvalid ""- parseSignatureInvalid "bbb"-- parseSignatureInvalid- "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"- parseSignatureInvalid- "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vB"-- parseSignatureInvalid- "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"- parseSignatureInvalid- "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3ckfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"-- parseSignatureInvalid- "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"- parseSignatureInvalid- "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbrh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"- ]- ]- , testCase "checkSignature" $ mapM_ checkSignatureSample sampleSignatures- ]--data SignatureData = SignatureData- { sdPublicKey :: Text- , sdBytes :: ByteString- , sdSignature :: Text- , sdValid :: Bool- }---- These signatures have been produced by `tezos-client`.-sampleSignatures :: [SignatureData]-sampleSignatures =- [ SignatureData- { sdPublicKey = "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"- , sdBytes = "\0"- , sdSignature = "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"- , sdValid = True- }- , SignatureData- { sdPublicKey = "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"- , sdBytes = "\0\0"- , sdSignature = "edsigtj8LhbJ2B3qhZvqzA49raG65dydFcWZW9b9L7ntF3bb29zxaBFFL8SM1jeBUY66hG122znyVA4wpzLdwxcNZwSK3Szu7iD"- , sdValid = True- }- , SignatureData- { sdPublicKey = "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"- , sdBytes = "kot"- , sdSignature = "edsigtrs8bK7vNfiR4Kd9dWasVa1bAWaQSu2ipnmLGZuwQa8ktCEMYVKqbWsbJ7zTS8dgYT9tiSUKorWCPFHosL5zPsiDwBQ6vb"- , sdValid = False- }- ]- <>- [ SignatureData- { sdPublicKey = "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"- , sdBytes = "\0"- , sdSignature = "spsig1Ng2bs4PXCbjaFGuojk9K5Pt3CkfbUZyHLLrBxHSmTqrUUxQggi4yJBit3Ljqnqr61UpdTewTLiu4schSCfZvaRwu412oZ"- -- TODO (#18): should be valid, but crypto is not implemented yet.- , sdValid = False- }- , SignatureData- { sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"- , sdBytes = "\0\0"- , sdSignature = "spsig1aP7D9oheiraNuM1NgziMPSPKS1F9kSWyFqkE8WigaeU5Uzb3LwY34F7Y7RsF6sY5ZfUda1NWdrC5V4KEfm9jeU1eniHmy"- -- TODO (#18): should be valid, but crypto is not implemented yet.- , sdValid = False- }- , SignatureData- { sdPublicKey = "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"- , sdBytes = "kot"- , sdSignature = "spsig1PJ9LG9ovbpVJ3CucFWL7iBaQZjqEWMvppgLjYiiSwzcxpuUqHr2BUVZDUwkmZKzMNDWJdgtyhYiicz197TbhS4LPpnxDY"- , sdValid = False- }- ]- <>- -- TODO (#18): add valid samples for P256- [ SignatureData- { sdPublicKey = "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"- , sdBytes = "kot"- , sdSignature = "p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"- , sdValid = False- }- ]--parsePublicKeySample :: Text -> Expectation-parsePublicKeySample publicKeyText =- parsePublicKey publicKeyText `shouldSatisfy` isRight--parsePublicKeyInvalid :: Text -> Expectation-parsePublicKeyInvalid invalidPublicKeyText =- parsePublicKey invalidPublicKeyText `shouldSatisfy` isLeft--parseSignatureSample :: Text -> Expectation-parseSignatureSample publicKeyText =- parseSignature publicKeyText `shouldSatisfy` isRight--parseSignatureInvalid :: Text -> Expectation-parseSignatureInvalid invalidSignatureText =- parseSignature invalidSignatureText `shouldSatisfy` isLeft--checkSignatureSample :: SignatureData -> Assertion-checkSignatureSample sd =- checkSignature publicKey signature (sdBytes sd) @?= sdValid sd- where- publicKey = partialParse parsePublicKey (sdPublicKey sd)- signature = partialParse parseSignature (sdSignature sd)--------------------------------------------------------------------------------- Key hashing-------------------------------------------------------------------------------unit_Key_Hashing :: Assertion-unit_Key_Hashing = mapM_ hashKeySample sampleKeyHashes--sampleKeyHashes :: [(Text, Text)]-sampleKeyHashes =- [ ( "edpkupH22qrz1sNQt5HSvWfRJFfyJ9dhNbZLptE6GR4JbMoBcACZZH"- , "tz1NaZzLvdDBLfV2LWC6F4SJfNV2jHdZJXkJ"- )- , ( "edpkuwTWKgQNnhR5v17H2DYHbfcxYepARyrPGbf1tbMoGQAj8Ljr3V"- , "tz1Yz3VPaCNB5FjhdEVnSoN8Xv3ZM8g2LYhw"- )- , ( "sppk7cdA7Afj8MvuBFrP6KsTLfbM5DtH9GwYaRZwCf5tBVCz6UKGQFR"- , "tz2EfqCbLmpfv7mNiLcMmhxAwdgHtPTcwR4W"- )- , ( "sppk7Ze7NMs6EHF2uB8qq8GrEgJvE9PWYkUijN3LcesafzQuGyniHBD"- , "tz2Darj3LyQzekU98ZK8diHvuyn1YYjcHpc6"- )- , ( "p2pk67K1dwkDFPB63RZU5H3SoMCvmJdKZDZszc7U4FiGKN2YypKdDCB"- , "tz3S7wbUwQV581kroR81fYbgDBskFicZ6czW"- )- , ( "p2pk68C6tJr7pNLvgBH63K3hBVoztCPCA36zcWhXFUGywQJTjYBfpxk"- , "tz3QEbmdCdsMcnUo2rNjXdbKwg5tyack3goN"- )- ]--hashKeySample :: (Text, Text) -> Assertion-hashKeySample (pkText, keyHashText) = hashKey pk @?= keyHash- where- pk = partialParse parsePublicKey pkText- keyHash = partialParse parseKeyHash keyHashText--------------------------------------------------------------------------------- Utils--------------------------------------------------------------------------------- If passed textual data is invalid, it's a programmer mistake.-partialParse :: (Text -> Either CryptoParseError a) -> Text -> a-partialParse parse = either (error . pretty) id . parse
− test/Test/Tezos/Crypto/Hash.hs
@@ -1,52 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Tezos.Crypto.Hash'.--module Test.Tezos.Crypto.Hash- ( test_Bytes_Hashing- ) where--import Fmt (fmt, hexF)-import Test.HUnit ((@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Tezos.Crypto.Hash--test_Bytes_Hashing :: [TestTree]-test_Bytes_Hashing =- [ testGroup "blake2b" $ hashingTest blake2b blake2bHashes- , testGroup "sha256" $ hashingTest sha256 sha256Hashes- , testGroup "sha512" $ hashingTest sha512 sha512Hashes- ]---- These values have been computed using the following contract:-{--parameter string;-storage bytes;-code { CDR; SHA512; # replace with desired function- NIL operation; PAIR;};---}--blake2bHashes, sha256Hashes, sha512Hashes :: [(ByteString, Text)]-blake2bHashes =- [ ("\0", "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314") -- 0x00- , ("\0\0", "9ee6dfb61a2fb903df487c401663825643bb825d41695e63df8af6162ab145a6") -- 0x0000- ]-sha256Hashes =- [ ("\0", "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d") -- 0x00- , ("\0\0", "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7") -- 0x0000- ]-sha512Hashes =- [ ("\0", "b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d22704a802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee") -- 0x00- , ("#", "d369286ac86b60fa920f6464d26becacd9f4c8bd885b783407cdcaa74fafd45a8b56b364b63f6256c3ceef26278a1c7799d4243a8149b5ede5ce1d890b5c7236") -- 0x23- ]--hashingTest :: (ByteString -> ByteString) -> [(ByteString, Text)] -> [TestTree]-hashingTest hashFunc pairs = do- flip map pairs $ \(bs, bsHashHex) -> do- testCase ("correctly computes hash of 0x" <> fmt (hexF bs)) $- fmt (hexF (hashFunc bs)) @?= bsHashHex
− test/Test/Typeable.hs
@@ -1,36 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests on 'Util.Typeable'.-module Test.Typeable- ( test_castIgnoringPhantom- ) where--import qualified Data.Kind as Kind-import Test.HUnit ((@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.HUnit (testCase)--import Util.Typeable--data Some = forall x. Typeable x => Some x--data MyType (a :: Kind.Type) = MyType Int- deriving stock (Show, Eq)--data MyType' (a :: Kind.Type) = MyType' Double- deriving stock (Show, Eq)--test_castIgnoringPhantom :: [TestTree]-test_castIgnoringPhantom =- [ testGroup "Simple type"- [ testCase "Can cast normal" $- case (Some $ MyType @Integer 5) of- Some x -> castIgnoringPhantom x @?= Just (MyType 5)-- , testCase "Cast fails on mismatch" $- case (Some $ MyType @Integer 5) of- Some x -> castIgnoringPhantom @MyType' x @?= Nothing- ]- ]
− test/Test/Typecheck.hs
@@ -1,287 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# LANGUAGE ViewPatterns #-}--module Test.Typecheck- ( unit_Good_contracts- , unit_Bad_contracts- , test_srcPosition- , unit_Unreachable_code- , test_Roundtrip- , test_StackRef- , test_TCTypeError_display- , hprop_ValueSeq_as_list- , test_SELF- , test_Value_contract- ) where--import Data.Default (def)-import qualified Data.Kind as Kind-import qualified Data.Map as M-import Data.Singletons (SingI(..))-import Data.Typeable (typeRep)-import Fmt (build, pretty)-import Hedgehog (Gen, Property, forAll, property, (===))-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.Hspec (expectationFailure)-import Test.Hspec.Expectations (Expectation)-import Test.HUnit (Assertion, assertFailure, (@?=))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.Hedgehog (testProperty)-import Test.Tasty.HUnit (testCase)--import Michelson.ErrorPos (InstrCallStack(..), LetName(..), Pos(..), SrcPos(..), srcPos)-import Michelson.Parser (utypeQ)-import Michelson.Runtime (prepareContract)-import Michelson.Test (failedTest, meanTimeUpperBoundPropNF, sec, total)-import Michelson.Test.Gen (genValueInt, genValueTimestamp)-import Michelson.Test.Import (ImportContractError(..), readContract)-import Michelson.TypeCheck-import qualified Michelson.Typed as T-import Michelson.Untyped (ParameterType(..), noAnn, unsafeBuildEpName)-import qualified Michelson.Untyped as Un-import Tezos.Address (Address(..), ContractHash, mkContractHashHack)-import Tezos.Core (Timestamp)-import Util.IO (readFileUtf8)--import Test.Util.Contracts (getIllTypedContracts, getWellTypedContracts, inContractsDir)--unit_Good_contracts :: Assertion-unit_Good_contracts = mapM_ (\f -> checkFile [] True f (const pass)) =<< getWellTypedContracts--unit_Bad_contracts :: Assertion-unit_Bad_contracts = mapM_ (\f -> checkFile [] False f (const pass)) =<< getIllTypedContracts--pattern IsSrcPos :: Word -> Word -> InstrCallStack-pattern IsSrcPos l c <- InstrCallStack [] (SrcPos (Pos l) (Pos c))--test_srcPosition :: [TestTree]-test_srcPosition =- [ testCase "Verify instruction position in a typecheck error" $ do- checkIllFile (inContractsDir "ill-typed/basic3.tz") $ \case- TCFailedOnInstr (Un.CONS _) _ (IsSrcPos 8 6) _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/testassert_invalid_stack3.mtz") $ \case- TCFailedOnInstr Un.DROP _ (IsSrcPos 10 17) _ (Just NotEnoughItemsOnStack) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/testassert_invalid_stack2.mtz") $ \case- TCExtError _ (IsSrcPos 9 2) (TestAssertError _) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/macro_in_let_fail.mtz") $ \case- TCFailedOnInstr (Un.COMPARE _) _ (InstrCallStack [LetName "cmpLet"] (SrcPos (Pos 7) (Pos 6))) _- (Just (TypeEqError _ _)) -> True- _ -> False- checkIllFile (inContractsDir "ill-typed/compare_annotation_mismatch.tz") $ \case- TCFailedOnInstr (Un.COMPARE _) _ _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_map_update.tz") $ \case- TCFailedOnInstr (Un.UPDATE _) (SomeHST (_ ::& _ ::& (T.NTMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_map_get.tz") $ \case- TCFailedOnInstr (Un.GET _) (SomeHST (_ ::& (T.NTMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_map_mem.tz") $ \case- TCFailedOnInstr (Un.MEM _) (SomeHST (_ ::& (T.NTMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_big_map_update.tz") $ \case- TCFailedOnInstr (Un.UPDATE _) (SomeHST (_ ::& _ ::& (T.NTBigMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_big_map_get.tz") $ \case- TCFailedOnInstr (Un.GET _) (SomeHST (_ ::& (T.NTBigMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_big_map_mem.tz") $ \case- TCFailedOnInstr (Un.MEM _) (SomeHST (_ ::& (T.NTBigMap{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_set_update.tz") $ \case- TCFailedOnInstr (Un.UPDATE _) (SomeHST (_ ::& _ ::& (T.NTSet{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False-- checkIllFile (inContractsDir "ill-typed/annotation_mismatch_set_mem.tz") $ \case- TCFailedOnInstr (Un.MEM _) (SomeHST (_ ::& (T.NTSet{}, _, _) ::& SNil)) _ _ (Just (AnnError _)) -> True- _ -> False- ]- where- unexpected f e =- expectationFailure $ "Unexpected typecheck error: " <> displayException e <> " in file: " <> f- checkIllFile file check = checkFile [] False file $- \e -> if check e then pass else unexpected file e--checkFile- :: HasCallStack- => [(ContractHash, ParameterType)]- -> Bool- -> FilePath- -> (TCError -> Expectation)- -> Expectation-checkFile originatedContracts wellTyped file onError = do- c <- prepareContract (Just file)- case doTC c of- Left err- | wellTyped ->- expectationFailure $- "Typechecker unexpectedly failed on " <> show file <> ": " <> displayException err- | otherwise -> onError err- Right _- | not wellTyped ->- assertFailure $- "Typechecker unexpectedly considered " <> show file <> " well-typed."- | otherwise -> pass- where- doTC = typeCheckContract (M.fromList originatedContracts)--unit_Unreachable_code :: Assertion-unit_Unreachable_code = do- let file = inContractsDir "ill-typed/fail_before_nop.tz"- let ics = InstrCallStack [] (srcPos 7 13)- econtract <- readContract @'T.TUnit @'T.TUnit file <$> readFileUtf8 file- econtract @?= Left (ICETypeCheck $ TCUnreachableCode ics (one $ Un.WithSrcEx ics $ Un.SeqEx []))--test_Roundtrip :: [TestTree]-test_Roundtrip =- [ testGroup "Value"- [ roundtripValue @Integer genValueInt- , roundtripValue @Timestamp genValueTimestamp- ]- ]- where- roundtripValue- :: forall (a :: Kind.Type).- ( Each [T.KnownT, T.HasNoOp] '[T.ToT a]- , Typeable a- )- => Gen (T.Value $ T.ToT a)- -> TestTree- roundtripValue genValue =- testProperty (show $ typeRep (Proxy @a)) $ property $ do- val :: T.Value (T.ToT a) <- forAll $ genValue- let uval = T.untypeValue val- runTC = runTypeCheckIsolated . usingReaderT (def @InstrCallStack)- case runTC $ typeCheckValue uval of- Right got -> got === val- Left err -> failedTest $- "Type check unexpectedly failed: " <> pretty err--test_StackRef :: [TestTree]-test_StackRef =- [ testProperty "Typecheck fails when ref is out of bounds" $ property $ do- let instr = printStRef 2- hst = stackEl ::& stackEl ::& SNil- case- runTypeCheck (error "no contract param") mempty $- typeCheckList [Un.WithSrcEx def $ Un.PrimEx instr] hst- of- Left err -> void $ total err- Right _ -> failedTest "Typecheck unexpectedly succeded"- , testProperty "Typecheck time is reasonably bounded" $- let hst = stackEl ::& SNil- run i =- case- runTypeCheck (error "no contract param") mempty $- typeCheckList [Un.WithSrcEx def $ Un.PrimEx (printStRef i)] hst- of- Left err -> err- Right _ -> error "Typecheck unexpectedly succeded"- -- Making code processing performance scale with code size looks like a- -- good property, so we'd like to avoid scenario when user tries to- -- access 100500-th element of stack and typecheck hangs as a result- in meanTimeUpperBoundPropNF (sec 1) run 100000000000- ]- where- printStRef i = Un.EXT . Un.UPRINT $ Un.PrintComment [Right (Un.StackRef i)]- stackEl = (T.starNotes @'T.TUnit, T.Dict, noAnn)--test_TCTypeError_display :: [TestTree]-test_TCTypeError_display =- -- One may say that it's madness to write tests on 'Buildable' instances,- -- but IMO (martoon) it's worth resulting duplication because tests allow- -- avoiding silly errors like lost spaces and ensuring general sanity- -- of used way to display content.- [ testCase "TypeEqError" $- build (TypeEqError T.TUnit T.TKey)- @?= "Types not equal: unit /= key"-- , testCase "StackEqError" $- build (StackEqError [T.TUnit, T.TBytes] [])- @?= "Stacks not equal: [unit, bytes] /= []"-- , testCase "UnsupportedTypes" $- build (UnsupportedTypeForScope (T.TBigMap T.TInt T.TInt) T.BtHasBigMap)- @?= "Type 'big_map int int' is unsupported here because it has 'big_map'"-- , testCase "InvalidValueType" $- build (InvalidValueType T.TUnit)- @?= "Value type is never a valid `unit`"- ]--hprop_ValueSeq_as_list :: Property-hprop_ValueSeq_as_list = property $ do- l <- forAll $ Gen.nonEmpty (Range.linear 0 100) (Gen.integral (Range.linearFrom 0 -1000 1000))- let- untypedValue = Un.ValueSeq $ Un.ValueInt <$> l- typedValue = T.VList $ T.VInt <$> toList l- runTypeCheckInstr = runTypeCheckIsolated . usingReaderT def- runTypeCheckInstr (typeCheckValue untypedValue) === Right typedValue--test_SELF :: [TestTree]-test_SELF =- [ testCase "Entrypoint not present" $- 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 (inContractsDir "ill-typed/self-entrypoint-type-mismatch.mtz")- (const pass)-- , testCase "Entrypoint can be found" $- checkFile [] True (inContractsDir "entrypoints/self1.mtz")- (const pass)- ]--test_Value_contract :: [TestTree]-test_Value_contract =- [ testCase "No contract exists" $- case doTypeCheckVal @('T.TContract 'T.TUnit) mempty addrUVal1 of- Left (TCFailedOnValue _ _ _ _ (Just (UnknownContract _))) -> pass- res -> assertFailure $ "Unexpected result: " <> show res-- , testCase "Entrypoint does not exist" $- case doTypeCheckVal @('T.TContract 'T.TKey) env1 addrUVal1 of- Left (TCFailedOnValue _ _ _ _ (Just (EntryPointNotFound _))) -> pass- res -> assertFailure $ "Unexpected result: " <> show res-- , testCase "Correct contract value" $- case doTypeCheckVal @('T.TContract 'T.TInt) env1 addrUVal2 of- Right _ -> pass- res -> assertFailure $ "Unexpected result: " <> show res- ]- where- addr1' = mkContractHashHack "123"- addr1 = T.EpAddress (ContractAddress addr1') (unsafeBuildEpName "a")- addr2 = T.EpAddress (ContractAddress addr1') (unsafeBuildEpName "q")- addrUVal1 = Un.ValueString $ T.mformatEpAddress addr1- addrUVal2 = Un.ValueString $ T.mformatEpAddress addr2-- env1 = M.fromList- [ ( addr1', ParameterType [utypeQ| ( nat %s | int %q ) |] noAnn)- ]-- doTypeCheckVal- :: SingI t- => TcOriginatedContracts -> Un.Value -> Either TCError (T.Value t)- doTypeCheckVal env uval =- runTypeCheck (error "hello there") env $ usingReaderT def $- typeCheckValue uval
− test/Test/Untyped/Instr.hs
@@ -1,47 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Tests for 'Michelson.Untyped.Instr'.--module Test.Untyped.Instr- ( unit_Flattening- ) where--import Test.HUnit (Assertion, (@?=))--import Michelson.Untyped.Instr--unit_Flattening :: Assertion-unit_Flattening = flattenExpandedOp sampleOp @?= expectedInstrs- where- sampleOp :: ExpandedOp- sampleOp = SeqEx- [ toSeq []- , toSeq seq1- , toPrim prim3- ]-- toPrim :: ExpandedInstr -> ExpandedOp- toPrim = PrimEx- toSeq :: [ExpandedInstr] -> ExpandedOp- toSeq = SeqEx . map PrimEx-- seq1 = [prim1, prim2]- prim1 = DROP- prim2 = SWAP- prim3 = DIP- [ toPrim prim1- , toSeq seq1- , toSeq seq1- , toSeq []- , SeqEx [toSeq seq1, toSeq seq1]- ]-- expectedInstrs :: [ExpandedInstr]- expectedInstrs =- [ prim1- , prim2- , DIP $ map toPrim $- prim1 : mconcat (replicate 4 seq1)- ]
− test/Test/Util/Contracts.hs
@@ -1,83 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Utility functions to read sample contracts (for testing).--module Test.Util.Contracts- ( 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 (\ext ->- concatMapM (getContractsWithExtension ext) illTypedContractDirs- ) [".tz", ".mtz"]--getWellTypedContracts :: IO [FilePath]-getWellTypedContracts = getWellTypedMichelsonContracts <> getWellTypedMorleyContracts--getUnparsableContracts :: IO [FilePath]-getUnparsableContracts =- concatMapM (flip getContractsWithExtension (contractsDir </> "unparsable"))- [".tz", ".mtz"]--getWellTypedMichelsonContracts :: IO [FilePath]-getWellTypedMichelsonContracts =- concatMapM (getContractsWithExtension ".tz") wellTypedContractDirs--getWellTypedMorleyContracts :: IO [FilePath]-getWellTypedMorleyContracts = concatMapM (getContractsWithExtension ".mtz") wellTypedContractDirs--getContractsWithExtension :: String -> FilePath -> IO [FilePath]-getContractsWithExtension ext dir = mapMaybe convertPath <$> listDirectory dir- where- convertPath :: FilePath -> Maybe FilePath- convertPath fileName- | (ext `isSuffixOf` fileName) =- Just (dir </> fileName)- | otherwise = Nothing--wellTypedContractDirs :: [FilePath]-wellTypedContractDirs =- contractsDir :- map ((contractsDir </> "tezos_examples") </>)- [ "attic"- , "entrypoints"- , "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 =- fmap attachPrettyPath <$> getContractsWithExtension ext fp- where- attachPrettyPath :: FilePath -> (FilePath, FilePath)- attachPrettyPath src = (src, addExtension src refExt)
− test/Test/Util/HUnit.hs
@@ -1,18 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Helpers for HUnit.--module Test.Util.HUnit- ( assertEqualBuild- ) where--import Test.HUnit (Assertion, assertEqual)-import Test.Util.Hedgehog (ShowThroughBuild(..))--assertEqualBuild- :: (HasCallStack, Eq a, Show (ShowThroughBuild a))- => String -> a -> a -> Assertion-assertEqualBuild desc a b =- assertEqual desc (STB a) (STB b)
− test/Test/Util/Hedgehog.hs
@@ -1,91 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{--Copyright (c) 2017 IOHK--Permission is hereby granted, free of charge, to any person obtaining-a copy of this software and associated documentation files (the-"Software"), to deal in the Software without restriction, including-without limitation the rights to use, copy, modify, merge, publish,-distribute, sublicense, and/or sell copies of the Software, and to-permit persons to whom the Software is furnished to do so, subject to-the following conditions:--The above copyright notice and this permission notice shall be included-in all copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.--}---- | Testing utilities to be used with Hedgehog--module Test.Util.Hedgehog- ( ShowThroughBuild (..)-- -- * Roundtrip properties- , roundtripTree- , roundtripTreeSTB- , aesonRoundtrip- ) where--import Data.Aeson (FromJSON(..), ToJSON(..))-import qualified Data.Aeson as Aeson-import Fmt (Buildable, pretty)-import Test.Tasty (TestTree)-import qualified Text.Show (show)--import Hedgehog (Gen)-import Michelson.Test.Util (roundtripTree)--------------------------------------------------------------------------------- 'Show'ing a value though 'Buildable' type class.--- Useful because Hedgehog uses 'Show'.-------------------------------------------------------------------------------newtype ShowThroughBuild a = STB- { unSTB :: a- } deriving newtype (Eq, Ord)--instance {-# OVERLAPPABLE #-} Buildable a => Show (ShowThroughBuild a) where- show = pretty . unSTB--instance Show (ShowThroughBuild ByteString) where- show = show . unSTB--genShowThroughBuild :: Gen a -> Gen (ShowThroughBuild a)-genShowThroughBuild genA = STB <$> genA---------------------------------------------------------------------------------- Roundtrip--------------------------------------------------------------------------------- | Version of 'roundtripTree' which shows values using 'Buildable' instance.-roundtripTreeSTB- :: forall x y err.- ( Show (ShowThroughBuild x)- , Show y- , Show (ShowThroughBuild err)- , Typeable x- , Eq x- , Eq err- )- => Gen x- -> (x -> y)- -> (y -> Either err x)- -> TestTree-roundtripTreeSTB genX xToY yToX = roundtripTree (genShowThroughBuild genX) (xToY . unSTB) (bimap STB STB . yToX)--aesonRoundtrip- :: forall x.- (Show (ShowThroughBuild x), ToJSON x, FromJSON x, Typeable x, Eq x)- => Gen x -> TestTree-aesonRoundtrip genX = roundtripTreeSTB genX (Aeson.encode @x) Aeson.eitherDecode
− test/Test/Util/Parser.hs
@@ -1,21 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--module Test.Util.Parser- ( shouldParse- ) where--import Test.HUnit.Base (assertFailure)-import Text.Megaparsec (errorBundlePretty)--import Michelson.Parser (Parser)-import qualified Michelson.Parser as Parser---- | Expect the given text to be successfully parsed.-shouldParse :: Parser a -> Text -> IO a-shouldParse parser text =- case Parser.parseNoEnv parser (toString text) text of- Left err -> assertFailure (errorBundlePretty err)- Right res -> return res-infix 2 `shouldParse`
− test/Test/ValConversion.hs
@@ -1,73 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ---- | Testing of toVal / fromVal conversions--module Test.ValConversion- ( test_Roundtrip- , unit_toVal- ) where--import Hedgehog (Gen)-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Test.HUnit (Assertion, (@?))-import Test.Tasty (TestTree)--import Michelson.Test.Util (genEither, genTuple2)-import Michelson.Text-import Michelson.Typed (IsoValue(..), ToT, Value, Value'(..))--import Test.Util.Hedgehog (roundtripTree)---- | TestTrees to test toVal / fromVal conversions (roundtrip)-test_Roundtrip :: [TestTree]-test_Roundtrip =- [ roundtrip @Integer genInteger- , roundtrip @Bool Gen.bool- , roundtrip @[Bool] $ Gen.list (Range.linear 0 100) Gen.bool- , roundtrip @(Maybe Integer) $ Gen.maybe genInteger- , roundtrip @(Maybe (Maybe Integer)) $ Gen.maybe (Gen.maybe genInteger)- , roundtrip @(Either Bool Integer) $ genEither Gen.bool genInteger- , roundtrip @(Set Integer) $ Gen.set (Range.linear 0 100) genInteger- , roundtrip @(Set Bool) $ Gen.set (Range.linear 0 2) Gen.bool- , roundtrip @(Map Integer Integer) $ Gen.map (Range.linear 0 100) (genTuple2 genInteger genInteger)- , roundtrip @(Map Integer Bool) $ Gen.map (Range.linear 0 100) (genTuple2 genInteger Gen.bool)- , roundtrip @(Map Integer (Maybe (Either Bool Bool))) $- Gen.map (Range.linear 0 100) $- genTuple2- genInteger- (Gen.maybe (genEither Gen.bool Gen.bool))- ]- where- genInteger = Gen.integral (Range.linearFrom 0 (fromIntegral $ minBound @Int64) (fromIntegral $ maxBound @Word64))-- roundtrip :: forall a.- (Show a, Eq a, Typeable a, IsoValue a) => Gen a -> TestTree- roundtrip genA = roundtripTree @a @_ @Void genA toVal (Right . fromVal)--unit_toVal :: Assertion-unit_toVal = do- check () $ (\case VUnit -> True;)- check (10 :: Integer) $ (\case (VInt 10) -> True; _ -> False)- check ("abc" :: ByteString) $ (\case (VBytes "abc") -> True; _ -> False)- check (Just "abc" :: Maybe ByteString)- $ (\case (VOption (Just (VBytes "abc"))) -> True; _ -> False)- check (Left "abc" :: Either ByteString ByteString)- $ (\case (VOr (Left (VBytes "abc"))) -> True; _ -> False)- check (Left "abc" :: Either ByteString Integer)- $ (\case (VOr (Left (VBytes "abc"))) -> True; _ -> False)- check ((10, "abc") :: (Integer, ByteString))- $ (\case (VPair ((VInt 10), VBytes "abc")) -> True; _ -> False)- check (["abc", "cde"] :: [ByteString])- $ (\case (VList [ VBytes "abc"- , VBytes "cde"]) -> True; _ -> False)- check @Integer 10 (\case (VInt 10) -> True; _ -> False)- check @Integer (-10) (\case (VInt (-10)) -> True; _ -> False)- check @Natural 10 (\case (VNat 10) -> True; _ -> False)- check [mt|abc|] (\case (VString [mt|abc|]) -> True; _ -> False)- check True (\case (VBool True) -> True; _ -> False)- where- check :: IsoValue a => a -> (Value (ToT a) -> Bool) -> Assertion- check v p = p (toVal v) @? "toVal returned unexpected result"
− test/Tree.hs
@@ -1,5 +0,0 @@--- SPDX-FileCopyrightText: 2020 Tocqueville Group------ SPDX-License-Identifier: LicenseRef-MIT-TQ--{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display -optF --generated-module -optF Tree #-}