diff --git a/README.md b/README.md index dbe39940a..1a7671378 100644 --- a/README.md +++ b/README.md @@ -42,16 +42,29 @@ Once Homebrew is installed, run the following command to install `pact`: brew install kadena-io/pact/pact ``` +However, see note [below](#z3) about z3 compatibility. + Installing Pact with Binary Distributions --- Pact can also be installed by following the instructions below: -- Install [z3](https://github.com/Z3Prover/z3/wiki) +- Install [z3](https://github.com/Z3Prover/z3/wiki). See note [below](#z3) about z3 compatibility. - Download the [prebuilt binaries](http://install.kadena.io/pact/downloads.html) for your distribution. Or see [Building](#Building) for instructions on how to build Pact from the source code. - Once you've downloaded the binary, make sure that it is marked as executable by running `chmod +x `. - Put the binary somewhere in your PATH. For installing `pact` on Linux distributions in the Arch family, refer to [this package on the AUR](https://aur.archlinux.org/packages/pact/). +z3 Compatibility +--- + +Pact users generally can use the production versions of Z3. However, with the current version at time of writing, 4.8.4, use of the function `str-to-int` can cause problems, and for those building from source or hacking Pact Haskell code, a unit test will fail/hang indefinitely because of this. + +The fix is to use the older version 4.8.3: +- For Mac homebrew users, 4.8.3 can be installed via `brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/a7e7806193f7605c7fef6110655911012d3f1eb3/Formula/z3.rb`. +- Z3 binaries are available [on github](https://github.com/Z3Prover/z3/releases/tag/z3-4.8.3) for manual installation (Pact uses the z3 in your `$PATH`). +- Nix builds install the correct version. + + Verifying Install --- @@ -110,19 +123,18 @@ verbose - [True|False] Provide extra logging information When running pact-serve with persistence enabled, the server automatically replays from the database `commands.sqlite` in the persist dir. To prevent replay, simply delete this file before starting the server. -Building +Building Pact --- -Building Pact used to require a working [Haskell Stack install](https://docs.haskellstack.org/en/stable/README/#how-to-install) . After which, building is as simple as 'stack build'. +### Building with Stack -To install for use with Atom and the command line, issue 'stack install' and then either add `$HOME/.local/bin` to your path, or symlink `$HOME/.local/bin/pact` somewhere in your PATH. +[Install stack](https://docs.haskellstack.org/en/stable/README/#how-to-install) -NOTE: We are currently transitioning to Nix build infrastructure. Stack builds -still work right now, but you should start transitioning to Nix using the -instructions below. +Issue `stack build` on the command line. -Building with Nix / NixOS ---- +Use `stack install` to install on the command line and for Atom, ensuring that `$HOME/.local/bin` is on your PATH. + +### Building with Nix / NixOS 1. Go to https://nixos.org/nix/, click "Get Nix", follow the instructions to install the Nix package manager. 2. Edit `$NIX_CONF_DIR/nix.conf`. @@ -156,7 +168,7 @@ sudo systemctl restart nix-daemon.service 5. Run `nix-build` from the project root. -### Incremental Builds +#### Incremental Builds Building with `nix-build` does a full rebuild every time, which is usually not what you want when developing. To do incremental builds, you need to enter a nix @@ -172,7 +184,7 @@ You can also build with stack inside this shell as follows: $ stack --stack-yaml stack-nix.yaml build ``` -### Hoogle Documentation +#### Hoogle Documentation Nix has out-of-the-box Hoogle integration. It allows you to run a local Hoogle server with docs for all of the project dependencies. This is really @@ -204,10 +216,6 @@ replace `[p.pact]` with a list of all the locally defined projects to include. For example: `[p.backend p.common p.frontend]` for a project that has those three separate local packages. -### z3 Troubleshooting - -Note for users of property and invariant verification: z3 version 4.8.4 hangs when checking `str-to-int` calls (this causes one test to fail). The fix is to use the older version 4.8.3, or a newer version in the future (4.8.4 is the latest release at time of writing). Our Nix install uses a working version. For non-Nix Mac users, `brew install` defaults to 4.8.4, but 4.8.3 can be installed via `brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/a7e7806193f7605c7fef6110655911012d3f1eb3/Formula/z3.rb`. For non-brew users, binaries are available [on github](https://github.com/Z3Prover/z3/releases/tag/z3-4.8.3) for manual installation (Pact uses the z3 in your `$PATH`). - License --- diff --git a/src-ghc/Pact/Bench.hs b/src-ghc/Pact/Bench.hs index 8919fff05..7ab294d3b 100644 --- a/src-ghc/Pact/Bench.hs +++ b/src-ghc/Pact/Bench.hs @@ -4,16 +4,20 @@ module Pact.Bench where import Control.Arrow +import Control.Concurrent import Control.DeepSeq import Control.Exception +import Control.Monad import Criterion.Main import Data.Aeson import Data.ByteString (ByteString) import Data.ByteString.Lazy (toStrict) import Data.Default +import qualified Data.HashMap.Strict as HM import qualified Data.Map.Strict as M import qualified Data.Set as S import System.CPUTime +import System.Directory import Unsafe.Coerce import Pact.Compile @@ -30,6 +34,9 @@ import Pact.Types.Logger import Pact.Types.PactValue import Pact.Types.RPC import Pact.Types.Runtime +import Pact.Native.Internal +import Pact.Persist.SQLite +import Pact.PersistPactDb hiding (db) longStr :: Int -> Text longStr n = pack $ "\"" ++ take n (cycle "abcdefghijklmnopqrstuvwxyz") ++ "\"" @@ -67,7 +74,7 @@ eitherDie = either (throwIO . userError) (return $!) entity :: Maybe EntityName entity = Just $ EntityName "entity" -loadBenchModule :: PactDbEnv e -> IO RefStore +loadBenchModule :: PactDbEnv e -> IO (ModuleData Ref,PersistModuleData) loadBenchModule db = do m <- pack <$> readFile "tests/bench/bench.pact" pc <- parseCode m @@ -77,7 +84,11 @@ loadBenchModule db = do pactInitialHash let e = setupEvalEnv db entity (Transactional 1) md initRefStore freeGasEnv permissiveNamespacePolicy noSPVSupport def - _erRefStore <$> evalExec e pc + void $ evalExec e pc + (benchMod,_) <- runEval def e $ getModule (def :: Info) (ModuleName "bench" Nothing) + p <- either (throwIO . userError . show) (return $!) $ traverse (traverse toPersistDirect) benchMod + return (benchMod,p) + parseCode :: Text -> IO ParsedCode parseCode m = ParsedCode m <$> eitherDie (parseExprs m) @@ -85,12 +96,13 @@ parseCode m = ParsedCode m <$> eitherDie (parseExprs m) benchNFIO :: NFData a => String -> IO a -> Benchmark benchNFIO bname = bench bname . nfIO -runPactExec :: PactDbEnv e -> RefStore -> ParsedCode -> IO Value -runPactExec dbEnv refStore pc = do +runPactExec :: Maybe (ModuleData Ref) -> PactDbEnv e -> ParsedCode -> IO Value +runPactExec benchMod dbEnv pc = do t <- Transactional . fromIntegral <$> getCPUTime let e = setupEvalEnv dbEnv entity t (initMsgData pactInitialHash) - refStore freeGasEnv permissiveNamespacePolicy noSPVSupport def - toJSON . _erOutput <$> evalExec e pc + initRefStore freeGasEnv permissiveNamespacePolicy noSPVSupport def + s = maybe def (initStateModules . HM.singleton (ModuleName "bench" Nothing)) benchMod + toJSON . _erOutput <$> evalExecState s e pc benchKeySet :: KeySet benchKeySet = KeySet [PublicKey "benchadmin"] (Name ">" def) @@ -98,18 +110,19 @@ benchKeySet = KeySet [PublicKey "benchadmin"] (Name ">" def) acctRow :: ObjectMap PactValue acctRow = ObjectMap $ M.fromList [("balance",PLiteral (LDecimal 100.0))] -benchRead :: Domain k v -> k -> Method () (Maybe v) -benchRead KeySets _ = rc (Just benchKeySet) -benchRead UserTables {} _ = rc (Just acctRow) -benchRead _ _ = rc Nothing +benchRead :: PersistModuleData -> Domain k v -> k -> Method () (Maybe v) +benchRead _ KeySets _ = rc (Just benchKeySet) +benchRead _ UserTables {} _ = rc (Just acctRow) +benchRead benchMod Modules _ = rc (Just benchMod) +benchRead _ _ _ = rc Nothing -benchReadValue :: Table k -> k -> Persist () (Maybe v) -benchReadValue (DataTable t) _k +benchReadValue :: PersistModuleData -> Table k -> k -> Persist () (Maybe v) +benchReadValue benchMod (DataTable t) _k | t == "SYS_keysets" = rcp $ Just (unsafeCoerce benchKeySet) | t == "USER_bench_bench-accounts" = rcp $ Just (unsafeCoerce acctRow) - | t == "SYS_modules" = rcp Nothing + | t == "SYS_modules" = rcp $ Just (unsafeCoerce benchMod) | otherwise = error (show t) -benchReadValue (TxTable _t) _k = rcp Nothing +benchReadValue _ (TxTable _t) _k = rcp Nothing mkBenchCmd :: [SomeKeyPair] -> (String, Text) -> IO (String, Command ByteString) @@ -127,24 +140,39 @@ main = do !parsedExps <- force <$> mapM (mapM (eitherDie . parseExprs)) exps !pureDb <- mkPureEnv neverLog initSchema pureDb - !refStore <- loadBenchModule pureDb + (benchMod',benchMod) <- loadBenchModule pureDb !benchCmd <- parseCode "(bench.bench)" - print =<< runPactExec pureDb refStore benchCmd - !mockDb <- mkMockEnv def { mockRead = MockRead benchRead } - !mdbRS <- loadBenchModule mockDb - print =<< runPactExec mockDb mdbRS benchCmd - !mockPersistDb <- mkMockPersistEnv neverLog def { mockReadValue = MockReadValue benchReadValue } - !mpdbRS <- loadBenchModule mockPersistDb - print =<< runPactExec mockPersistDb mpdbRS benchCmd + print =<< runPactExec Nothing pureDb benchCmd + !mockDb <- mkMockEnv def { mockRead = MockRead (benchRead benchMod) } + void $ loadBenchModule mockDb + print =<< runPactExec Nothing mockDb benchCmd + !mockPersistDb <- mkMockPersistEnv neverLog def { mockReadValue = MockReadValue (benchReadValue benchMod) } + void $ loadBenchModule mockPersistDb + print =<< runPactExec Nothing mockPersistDb benchCmd cmds_ <- traverse (mkBenchCmd [keyPair]) exps !cmds <- return $!! cmds_ - - - defaultMain [ - benchParse, - benchCompile parsedExps, - benchVerify cmds, - benchNFIO "puredb" (runPactExec pureDb refStore benchCmd), - benchNFIO "mockdb" (runPactExec mockDb mdbRS benchCmd), - benchNFIO "mockpersist" (runPactExec mockPersistDb mpdbRS benchCmd) + let sqliteFile = "log/bench.sqlite" + sqliteDb <- mkSQLiteEnv (newLogger neverLog "") True (SQLiteConfig sqliteFile []) neverLog + initSchema sqliteDb + void $ loadBenchModule sqliteDb + print =<< runPactExec Nothing sqliteDb benchCmd + + let cleanupSqlite = do + c <- readMVar $ pdPactDbVar sqliteDb + void $ closeSQLite $ _db c + removeFile sqliteFile + sqlEnv b = envWithCleanup (return ()) (const cleanupSqlite) (const b) + + defaultMain + [ benchParse + , benchCompile parsedExps + , benchVerify cmds + , benchNFIO "puredb" (runPactExec Nothing pureDb benchCmd) + , benchNFIO "mockdb" (runPactExec Nothing mockDb benchCmd) + , benchNFIO "mockpersist" (runPactExec Nothing mockPersistDb benchCmd) + , benchNFIO "sqlite" (runPactExec Nothing sqliteDb benchCmd) + , benchNFIO "puredb-withmod" (runPactExec (Just benchMod') pureDb benchCmd) + , benchNFIO "mockdb-withmod" (runPactExec (Just benchMod') mockDb benchCmd) + , benchNFIO "mockpersist-withmod" (runPactExec (Just benchMod') mockPersistDb benchCmd) + , sqlEnv $ benchNFIO "sqlite-withmod" (runPactExec (Just benchMod') sqliteDb benchCmd) ] diff --git a/src-ghc/Pact/Interpreter.hs b/src-ghc/Pact/Interpreter.hs index de9d6bec6..b1494c403 100644 --- a/src-ghc/Pact/Interpreter.hs +++ b/src-ghc/Pact/Interpreter.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE TupleSections #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RecordWildCards #-} -- | "Production" interpreter for Pact, as opposed to the REPL. @@ -17,6 +18,7 @@ module Pact.Interpreter , MsgData(..) , EvalResult(..) , initMsgData + , initStateModules , evalExec , evalExecState , evalContinuation @@ -33,10 +35,10 @@ module Pact.Interpreter import Control.Concurrent import Control.Monad.Catch import Control.Monad.Except +import Control.Lens import Data.Aeson import Data.Default -import qualified Data.HashMap.Strict as HM -import Data.Maybe +import Data.HashMap.Strict (HashMap) import qualified Data.Set as S import System.Directory @@ -72,9 +74,9 @@ data EvalResult = EvalResult { _erInput :: !(Either PactContinuation [Term Name]) , _erOutput :: ![PactValue] , _erLogs :: ![TxLog Value] - , _erRefStore :: !RefStore , _erExec :: !(Maybe PactExec) , _erGas :: Gas + , _erLoadedModules :: HashMap ModuleName (ModuleData Ref,Bool) } deriving (Eq,Show) @@ -86,6 +88,9 @@ evalExecState initState evalEnv ParsedCode {..} = do terms <- throwEither $ compileExps (mkTextInfo _pcCode) _pcExps interpret initState evalEnv (Right terms) +-- | For pre-installing modules into state. +initStateModules :: HashMap ModuleName (ModuleData Ref) -> EvalState +initStateModules modules = set (evalRefs . rsLoadedModules) (fmap (,False) modules) def evalContinuation :: EvalEnv e -> PactContinuation -> IO EvalResult evalContinuation ee pact = evalContinuationState def ee pact @@ -125,7 +130,7 @@ setupEvalEnv dbEnv ent mode msgData refStore gasEnv np spv pd = modeToTx Local = Nothing initRefStore :: RefStore -initRefStore = RefStore nativeDefs HM.empty +initRefStore = RefStore nativeDefs mkSQLiteEnv :: Logger -> Bool -> PSL.SQLiteConfig -> Loggers -> IO (PactDbEnv (DbEnv PSL.SQLite)) mkSQLiteEnv initLog deleteOldFile c loggers = do @@ -153,12 +158,10 @@ interpret initState evalEnv terms = do ((rs,logs),state) <- runEval initState evalEnv $ evalTerms tx terms let gas = _evalGas state - refStore = newRefs . _eeRefStore $ evalEnv pactExec = _evalPactExec state - newRefs oldStore | isNothing tx = oldStore - | otherwise = updateRefStore (_evalRefs state) oldStore + modules = _rsLoadedModules $ _evalRefs state -- output uses lenient conversion - return $! EvalResult terms (map toPactValueLenient rs) logs refStore pactExec gas + return $! EvalResult terms (map toPactValueLenient rs) logs pactExec gas modules evalTerms :: Maybe TxId -> Either PactContinuation [Term Name] -> Eval e ([Term Name],[TxLog Value]) evalTerms tx terms = do diff --git a/src-ghc/Pact/MockDb.hs b/src-ghc/Pact/MockDb.hs index 657702597..886ea09a4 100644 --- a/src-ghc/Pact/MockDb.hs +++ b/src-ghc/Pact/MockDb.hs @@ -17,7 +17,7 @@ newtype MockRead = instance Default MockRead where def = MockRead (\_t _k -> rc Nothing) newtype MockKeys = - MockKeys (TableName -> Method () [RowKey]) + MockKeys (forall k v . (IsString k,AsString k) => Domain k v -> Method () [k]) instance Default MockKeys where def = MockKeys (\_t -> rc []) newtype MockTxIds = diff --git a/src-ghc/Pact/PersistPactDb/Regression.hs b/src-ghc/Pact/PersistPactDb/Regression.hs index 6da4193a8..fe7cfb8ee 100644 --- a/src-ghc/Pact/PersistPactDb/Regression.hs +++ b/src-ghc/Pact/PersistPactDb/Regression.hs @@ -33,11 +33,12 @@ loadModule = do let mn = ModuleName "simple" Nothing case r of Left a -> throwFail $ "module load failed: " ++ show a - Right _ -> case preview (rEvalState . evalRefs . rsNewModules . ix mn) s of - Just md -> case traverse (traverse toPersistDirect) md of + Right _ -> case preview (rEvalState . evalRefs . rsLoadedModules . ix mn) s of + Just (md,_) -> case traverse (traverse toPersistDirect) md of Right md' -> return (mn,md,md') Left e -> throwFail $ "toPersistDirect failed: " ++ show e - Nothing -> throwFail $ "Failed to find module 'simple': " ++ show (view (rEvalState . evalRefs . rsNewModules) s) + Nothing -> throwFail $ "Failed to find module 'simple': " ++ + show (view (rEvalState . evalRefs . rsLoadedModules) s) nativeLookup :: NativeDefName -> Maybe (Term Name) nativeLookup (NativeDefName n) = case HM.lookup (Name n def) nativeDefs of @@ -106,10 +107,10 @@ runRegression p = do _getTxLog pactdb usert (head tids) v _writeRow pactdb Insert usert "key2" row v assertEquals' "user insert key2 pre-rollback" (Just row) (_readRow pactdb usert "key2" v) - assertEquals' "keys pre-rollback" ["key1","key2"] $ _keys pactdb user1 v + assertEquals' "keys pre-rollback" ["key1","key2"] $ _keys pactdb (UserTables user1) v _rollbackTx pactdb v assertEquals' "rollback erases key2" Nothing $ _readRow pactdb usert "key2" v - assertEquals' "keys" ["key1"] $ _keys pactdb user1 v + assertEquals' "keys" ["key1"] $ _keys pactdb (UserTables user1) v return v toTerm' :: ToTerm a => a -> Term Name diff --git a/src-ghc/Pact/ReplTools.hs b/src-ghc/Pact/ReplTools.hs index 4ae983b84..8221ed0bd 100644 --- a/src-ghc/Pact/ReplTools.hs +++ b/src-ghc/Pact/ReplTools.hs @@ -35,12 +35,12 @@ import Pact.Repl.Types interactiveRepl :: IO (Either () (Term Name)) interactiveRepl = generalRepl Interactive --- Note(emily): revisit whether we want all _module_ names, or all interface names as well. +-- | Complete function names for _loaded_ modules completeFn :: (MonadIO m, MonadState ReplState m) => CompletionFunc m completeFn = completeQuotedWord (Just '\\') "\"" listFiles $ completeWord (Just '\\') ("\"\'" ++ filenameWordBreakChars) $ \str -> do - modules <- use (rEnv . eeRefStore . rsModules) - let namesInModules = toListOf (traverse . mdRefMap . to HM.keys . each) modules + modules <- use (rEvalState . evalRefs . rsLoadedModules) + let namesInModules = toListOf (traverse . _1 . mdRefMap . to HM.keys . each) modules allNames = concat [ namesInModules , nameOfModule <$> HM.keys modules diff --git a/src-ghc/Pact/Server/PactService.hs b/src-ghc/Pact/Server/PactService.hs index ac3d95bed..72d787bde 100644 --- a/src-ghc/Pact/Server/PactService.hs +++ b/src-ghc/Pact/Server/PactService.hs @@ -48,7 +48,7 @@ initPactService CommandConfig {..} loggers = do blockTime = 0 let mkCEI p@PactDbEnv {..} = do - cmdVar <- newMVar (CommandState initRefStore M.empty) + cmdVar <- newMVar (CommandState M.empty) klog "Creating Pact Schema" initSchema p return CommandExecInterface @@ -103,16 +103,16 @@ applyExec :: RequestKey -> PactHash -> [Signer] -> ExecMsg ParsedCode -> Command applyExec rk hsh signers (ExecMsg parsedCode edata) = do CommandEnv {..} <- ask when (null (_pcExps parsedCode)) $ throwCmdEx "No expressions found" - (CommandState refStore pacts) <- liftIO $ readMVar _ceState + (CommandState pacts) <- liftIO $ readMVar _ceState let sigs = userSigsToPactKeySet signers evalEnv = setupEvalEnv _ceDbEnv _ceEntity _ceMode (MsgData sigs edata Nothing (toUntypedHash hsh)) - refStore _ceGasEnv permissiveNamespacePolicy noSPVSupport _cePublicData + initRefStore _ceGasEnv permissiveNamespacePolicy noSPVSupport _cePublicData EvalResult{..} <- liftIO $ evalExec evalEnv parsedCode newCmdPact <- join <$> mapM (handlePactExec _erInput) _erExec let newPacts = case newCmdPact of Nothing -> pacts Just cmdPact -> M.insert (_pePactId cmdPact) cmdPact pacts - void $ liftIO $ swapMVar _ceState $ CommandState _erRefStore newPacts + void $ liftIO $ swapMVar _ceState $ CommandState newPacts mapM_ (\p -> liftIO $ logLog _ceLogger "DEBUG" $ "applyExec: new pact added: " ++ show p) newCmdPact return $ jsonResult _ceMode rk _erGas $ CommandSuccess (last _erOutput) @@ -146,7 +146,7 @@ applyContinuation rk hsh signers msg@ContMsg{..} = do let sigs = userSigsToPactKeySet signers pactStep = Just $ PactStep _cmStep _cmRollback _cmPactId (fmap (fmap fromPactValue) _peYield) evalEnv = setupEvalEnv _ceDbEnv _ceEntity _ceMode - (MsgData sigs _cmData pactStep (toUntypedHash hsh)) _csRefStore + (MsgData sigs _cmData pactStep (toUntypedHash hsh)) initRefStore _ceGasEnv permissiveNamespacePolicy noSPVSupport _cePublicData res <- tryAny (liftIO $ evalContinuation evalEnv _peContinuation) @@ -165,7 +165,7 @@ rollbackUpdate :: CommandEnv p -> ContMsg -> CommandState -> CommandM p () rollbackUpdate CommandEnv{..} ContMsg{..} CommandState{..} = do -- if step doesn't have a rollback function, no error thrown. Therefore, pact will be deleted -- from state. - let newState = CommandState _csRefStore $ M.delete _cmPactId _csPacts + let newState = CommandState $ M.delete _cmPactId _csPacts liftIO $ logLog _ceLogger "DEBUG" $ "applyContinuation: rollbackUpdate: reaping pact " ++ show _cmPactId void $ liftIO $ swapMVar _ceState newState @@ -174,7 +174,7 @@ continuationUpdate :: CommandEnv p -> ContMsg -> CommandState -> PactExec -> Com continuationUpdate CommandEnv{..} ContMsg{..} CommandState{..} newPactExec@PactExec{..} = do let nextStep = succ _cmStep isLast = nextStep >= _peStepCount - updateState pacts = CommandState _csRefStore pacts -- never loading modules during continuations + updateState pacts = CommandState pacts -- never loading modules during continuations if isLast then do liftIO $ logLog _ceLogger "DEBUG" $ "applyContinuation: continuationUpdate: reaping pact: " diff --git a/src-ghc/Pact/Types/Server.hs b/src-ghc/Pact/Types/Server.hs index f05e49531..b6b8b10d4 100644 --- a/src-ghc/Pact/Types/Server.hs +++ b/src-ghc/Pact/Types/Server.hs @@ -24,7 +24,7 @@ module Pact.Types.Server ( userSigToPactPubKey, userSigsToPactKeySet , CommandConfig(..), ccSqlite, ccEntity, ccGasLimit, ccGasRate - , CommandState(..), csRefStore, csPacts + , CommandState(..), csPacts , CommandEnv(..), ceEntity, ceMode, ceDbEnv, ceState, ceLogger, cePublicData, ceGasEnv , CommandM, runCommand, throwCmdEx , History(..) @@ -83,9 +83,8 @@ $(makeLenses ''CommandConfig) -data CommandState = CommandState { - _csRefStore :: RefStore - , _csPacts :: M.Map PactId PactExec +newtype CommandState = CommandState { + _csPacts :: M.Map PactId PactExec } deriving Show $(makeLenses ''CommandState) diff --git a/src/Pact/Analyze/Remote/Server.hs b/src/Pact/Analyze/Remote/Server.hs index 77399bb59..b9e7bb184 100644 --- a/src/Pact/Analyze/Remote/Server.hs +++ b/src/Pact/Analyze/Remote/Server.hs @@ -34,13 +34,10 @@ import qualified Text.Megaparsec.Char as MP import qualified Pact.Analyze.Check as Check import Pact.Analyze.Remote.Types (Request(..), Response(..), ClientError(..)) -import Pact.Repl (initReplState, evalRepl') -import Pact.Repl.Types (LibState, ReplMode(StringEval), - ReplState, rEnv) +import Pact.Repl (initReplState, evalRepl', replGetModules) +import Pact.Repl.Types import Pact.Types.Info (Code(_unCode)) -import Pact.Types.Runtime (Domain(KeySets), Method, - ModuleData, PactDb(_readRow), - eePactDb, eeRefStore, rsModules) +import Pact.Types.Runtime import Pact.Types.Term (ModuleDef(..), moduleDefName, moduleDefCode, ModuleName(..), Name(..), KeySet(..),Ref) @@ -90,8 +87,8 @@ initializeRepl = do pure $ rs & rEnv . eePactDb .~ dbImpl { _readRow = _readRow' } -replStateModules :: ReplState -> HM.HashMap ModuleName (ModuleData Ref) -replStateModules replState = replState ^. rEnv . eeRefStore . rsModules +replStateModules :: ReplState -> IO (Either PactError (HM.HashMap ModuleName (ModuleData Ref))) +replStateModules replState = fmap fst <$> replGetModules replState -- | Parser for strings like: @:2:2: Module "mod2" not found@ moduleNotFoundP :: MP.Parsec Void String ModuleName @@ -116,30 +113,31 @@ loadModules mods0 = do -- still need to be loaded -- - try again. if we have promoted more times than we have modules left, -- we've encountered a cycle and exit. - go mods replState promotionsSinceLastSuccess = do + go mods replState promotionsSinceLastSuccess lastLoaded = do (eSuccess, replState') <- runStateT (runExceptT (traverse_ loadModule mods)) replState - case eSuccess of - Left msg -> - if promotionsSinceLastSuccess >= length mods - then pure $ Left $ ClientError "detected cycle in modules" - else - case MP.parseMaybe moduleNotFoundP msg of - Nothing -> pure $ Left $ ClientError msg - Just depName -> do - let numLoaded = HM.size (replStateModules replState') - - HM.size (replStateModules replState) - case promoteBy ((== depName) . moduleDefName) (drop numLoaded mods) of - Nothing -> pure $ Left $ ClientError msg - Just mods' -> do - let promos' = if numLoaded > 0 then 0 else succ promotionsSinceLastSuccess - go mods' replState' promos' - - Right () -> - pure $ Right $ replStateModules replState' + replStateModules replState' >>= \rsm -> case rsm of + Left e -> pure $ Left $ ClientError $ show e + Right ms -> case eSuccess of + Left msg -> + if promotionsSinceLastSuccess >= length mods + then pure $ Left $ ClientError "detected cycle in modules" + else + case MP.parseMaybe moduleNotFoundP msg of + Nothing -> pure $ Left $ ClientError msg + Just depName -> do + let numLoaded = HM.size ms - lastLoaded + case promoteBy ((== depName) . moduleDefName) (drop numLoaded mods) of + Nothing -> pure $ Left $ ClientError msg + Just mods' -> do + let promos' = if numLoaded > 0 then 0 else succ promotionsSinceLastSuccess + go mods' replState' promos' (HM.size ms) + + Right () -> + pure $ Right ms replState0 <- initializeRepl - go mods0 replState0 0 + go mods0 replState0 0 0 where -- Promotes a value to the front of the list if it passes a test. diff --git a/src/Pact/Eval.hs b/src/Pact/Eval.hs index c77b1f670..0ebe2324a 100644 --- a/src/Pact/Eval.hs +++ b/src/Pact/Eval.hs @@ -29,11 +29,10 @@ module Pact.Eval (eval ,evalBeginTx,evalRollbackTx,evalCommitTx ,reduce,reduceBody - ,resolveFreeVars,resolveArg,resolveRef + ,resolveFreeVars,resolveArg,resolveRef,lookupModule ,enforceKeySet,enforceKeySetName ,checkUserType ,deref - ,installModule ,runPure,runReadOnly,Purity ,liftTerm,apply ,preGas @@ -94,11 +93,12 @@ evalCommitTx i = do enforceKeySetName :: Info -> KeySetName -> Eval e () enforceKeySetName mi mksn = do ks <- maybe (evalError mi $ "No such keyset: " <> pretty mksn) return =<< readRow mi KeySets mksn - runPure $ enforceKeySet mi (Just mksn) ks + runReadOnly mi $ enforceKeySet mi (Just mksn) ks {-# INLINE enforceKeySetName #-} --- | Enforce keyset against environment -enforceKeySet :: PureNoDb e => Info -> +-- | Enforce keyset against environment. +-- Runs as "read only" as custom predicate might require module load. +enforceKeySet :: PureReadOnly e => Info -> Maybe KeySetName -> KeySet -> Eval e () enforceKeySet i ksn KeySet{..} = do sigs <- view eeMsgSigs @@ -233,6 +233,28 @@ evalNamespace info setter m = do Nothing -> ModuleName nn (Just n) Just {} -> mn +-- | Lookup module in state or database with exact match on 'ModuleName'. +lookupModule :: HasInfo i => i -> ModuleName -> Eval e (Maybe (ModuleData Ref)) +lookupModule i mn = do + loaded <- preuse $ evalRefs . rsLoadedModules . ix mn + case loaded of + Just (m,_) -> return $ Just m + Nothing -> do + stored <- readRow (getInfo i) Modules mn + case stored of + Just mdStored -> do + natives <- view $ eeRefStore . rsNatives + let natLookup (NativeDefName n) = case HM.lookup (Name n def) natives of + Just (Direct t) -> Just t + _ -> Nothing + case traverse (traverse (fromPersistDirect natLookup)) mdStored of + Right md -> do + evalRefs . rsLoadedModules %= HM.insert mn (md,False) + return $ Just md + Left e -> evalError' i $ "Internal error: module restore failed: " <> pretty e + Nothing -> return Nothing + + -- | Evaluate top-level term. eval :: Term Name -> Eval e (Term Name) eval (TUse u@Use{..} i) = topLevelCall i "use" (GUse _uModuleName _uModuleHash) $ \g -> @@ -242,7 +264,7 @@ eval (TModule (MDModule m) bod i) = -- prepend namespace def to module name mangledM <- evalNamespace i mName m -- enforce old module keysets - oldM <- preview $ eeRefStore . rsModules . ix (_mName m) + oldM <- lookupModule i (_mName m) case oldM of Nothing -> return () Just (ModuleData omd _) -> @@ -268,10 +290,8 @@ eval (TModule (MDInterface m) bod i) = -- prepend namespace def to module name mangledI <- evalNamespace i interfaceName m -- enforce no upgrades - oldI <- readRow i Modules $ _interfaceName mangledI - case oldI of - Nothing -> return () - Just _old -> evalError i $ "Existing interface found (interfaces cannot be upgraded)" + void $ lookupModule i (_interfaceName mangledI) >>= traverse + (const $ evalError i $ "Existing interface found (interfaces cannot be upgraded)") (g,govI) <- loadInterface mangledI bod i gas writeRow i Write Modules (_interfaceName mangledI) =<< traverse (traverse toPersistDirect') govI return (g, msg $ "Loaded interface " <> pretty (_interfaceName mangledI)) @@ -291,7 +311,7 @@ evalContinuation (PactContinuation d args) = evalUse :: Use -> Eval e () evalUse (Use mn h i) = do - mm <- resolveName mn + mm <- resolveModule i mn case mm of Nothing -> evalError i $ "Module " <> pretty mn <> " not found" Just md -> do @@ -310,7 +330,7 @@ evalUse (Use mn h i) = do "Interfaces should not have associated hashes: " <> pretty _interfaceName - installModule md + installModule False md mangleDefs :: ModuleName -> Term Name -> Term Name mangleDefs mn term = modifyMn term @@ -349,8 +369,7 @@ loadModule m@Module {} bod1 mi g0 = do (m', solvedDefs) <- evaluateConstraints mi m evaluatedDefs mGov <- resolveGovernance solvedDefs m' let md = ModuleData mGov solvedDefs - installModule md - (evalRefs . rsNewModules) %= HM.insert (_mName m) md + installModule True md return (g1,md) resolveGovernance :: HM.HashMap Text Ref @@ -387,8 +406,7 @@ loadInterface i@Interface{..} body info gas0 = do mapM_ evalUse _interfaceImports evaluatedDefs <- evaluateDefs info (fmap (mangleDefs _interfaceName) idefs) let md = ModuleData (MDInterface i) evaluatedDefs - installModule md - (evalRefs . rsNewModules) %= HM.insert _interfaceName md + installModule True md return (gas1,md) -- | Definitions are transformed such that all free variables are resolved either to @@ -418,7 +436,7 @@ mkSomeDoc = either (SomeDoc . pretty) (SomeDoc . pretty) traverseGraph :: HM.HashMap Text (Term Name) -> Eval e [SCC (Term (Either Text Ref), Text, [Text])] traverseGraph defs = fmap stronglyConnCompR $ forM (HM.toList defs) $ \(dn,d) -> do d' <- forM d $ \(f :: Name) -> do - dm <- resolveRef f + dm <- resolveRef f f case (dm, f) of (Just t, _) -> return (Right t) (Nothing, Name fn _) -> @@ -438,7 +456,7 @@ evaluateConstraints info m evalMap = foldM evaluateConstraint (m, evalMap) $ _mInterfaces m where evaluateConstraint (m', refMap) ifn = do - refData <- resolveName ifn + refData <- resolveModule info ifn case refData of Nothing -> evalError info $ "Interface not defined: " <> pretty ifn @@ -457,33 +475,48 @@ solveConstraint -> Eval e (HM.HashMap Text Ref) -> Eval e (HM.HashMap Text Ref) solveConstraint info refName (Direct t) _ = - evalError info $ "found native reference " <> pretty t <> " while resolving module contraints: " <> pretty refName + evalError info $ "found native reference " <> pretty t + <> " while resolving module contraints: " <> pretty refName solveConstraint info refName (Ref t) evalMap = do em <- evalMap case HM.lookup refName em of Nothing -> case t of TConst{..} -> evalMap - _ -> evalError info $ "found unimplemented member while resolving model constraints: " <> pretty refName + _ -> evalError info $ + "found unimplemented member while resolving model constraints: " <> pretty refName Just (Direct s) -> - evalError info $ "found native reference " <> pretty s <> " while resolving module contraints: " <> pretty t + evalError info $ "found native reference " <> pretty s <> + " while resolving module contraints: " <> pretty t Just (Ref s) -> case (t, s) of (TDef (Def _n _mn dt (FunType args rty) _ m _) _, TDef (Def _n' _mn' dt' (FunType args' rty') _ _ _) _) -> do - when (dt /= dt') $ evalError info $ "deftypes mismatching: " <> pretty dt <> line <> pretty dt' - when (rty /= rty') $ evalError info $ "return types mismatching: " <> pretty rty <> line <> pretty rty' - when (length args /= length args') $ evalError info $ "mismatching argument lists: " <> prettyList args <> line <> prettyList args' + when (dt /= dt') $ evalError info $ "deftypes mismatching: " + <> pretty dt <> line <> pretty dt' + when (rty /= rty') $ evalError info $ "return types mismatching: " + <> pretty rty <> line <> pretty rty' + when (length args /= length args') $ evalError info $ "mismatching argument lists: " + <> prettyList args <> line <> prettyList args' forM_ (args `zip` args') $ \((Arg n ty _), (Arg n' ty' _)) -> do - when (n /= n') $ evalError info $ "mismatching argument names: " <> pretty n <> " and " <> pretty n' - when (ty /= ty') $ evalError info $ "mismatching types: " <> pretty ty <> " and " <> pretty ty' + when (n /= n') $ evalError info $ "mismatching argument names: " + <> pretty n <> " and " <> pretty n' + when (ty /= ty') $ evalError info $ "mismatching types: " + <> pretty ty <> " and " <> pretty ty' -- the model concatenation step: we reinsert the ref back into the map with new models pure $ HM.insert refName (Ref $ over (tDef . dMeta) (<> m) s) em _ -> evalError info $ "found overlapping const refs - please resolve: " <> pretty t -resolveName :: ModuleName -> Eval e (Maybe (ModuleData Ref)) -resolveName mn = do - md <- preview $ eeRefStore . rsModules . ix mn +-- | Lookup module in state or db, resolving against current namespace if unqualified. +resolveModule :: HasInfo i => i -> ModuleName -> Eval e (Maybe (ModuleData Ref)) +resolveModule = moduleResolver lookupModule + +-- | Perform some lookup involving a 'ModuleName' which if unqualified +-- will re-perform the lookup with the current namespace, if any +moduleResolver :: HasInfo i => (i -> ModuleName -> Eval e (Maybe a)) -> + i -> ModuleName -> Eval e (Maybe a) +moduleResolver lkp i mn = do + md <- lkp i mn case md of Just _ -> return md Nothing -> do @@ -492,28 +525,21 @@ resolveName mn = do Nothing -> do mNs <- use $ evalRefs . rsNamespace case mNs of - Just ns -> preview $ eeRefStore . rsModules . ix (set mnNamespace (Just . _nsName $ ns) mn) + Just ns -> lkp i $ set mnNamespace (Just . _nsName $ ns) mn Nothing -> pure Nothing -resolveRef :: Name -> Eval e (Maybe Ref) -resolveRef (QName q n _) = do - let lookupQn q' n' = preview $ eeRefStore . rsModules . ix q' . mdRefMap . ix n' - dsm <- lookupQn q n - case dsm of - d@Just {} -> return d - Nothing -> do - case (_mnNamespace q) of - Just {} -> pure Nothing -- explicit namespace not found - Nothing -> do - mNs <- use $ evalRefs . rsNamespace - case mNs of - Just ns -> lookupQn (set mnNamespace (Just $ _nsName ns) q) n - Nothing -> pure Nothing -- no explicit namespace or decalared namespace -resolveRef nn@(Name _ _) = do + +resolveRef :: HasInfo i => i -> Name -> Eval e (Maybe Ref) +resolveRef i (QName q n _) = moduleResolver (lookupQn n) i q + where + lookupQn n' i' q' = do + m <- lookupModule i' q' + return $ join $ HM.lookup n' . _mdRefMap <$> m +resolveRef _i nn@(Name _ _) = do nm <- preview $ eeRefStore . rsNatives . ix nn case nm of d@Just {} -> return d - Nothing -> preview (evalRefs . rsLoaded . ix nn) <$> get + Nothing -> preuse $ evalRefs . rsLoaded . ix nn -- | This should be impure. See 'evaluateDefs'. Refs are -- expected to exist, and if they don't, it is a serious bug @@ -522,13 +548,13 @@ unify _ (Right r) = r unify m (Left t) = m HM.! t evalConsts :: PureNoDb e => Ref -> Eval e Ref -evalConsts (Ref r) = case r of - c@TConst {..} -> case _tConstVal of +evalConsts rr@(Ref r) = case r of + TConst {..} -> case _tConstVal of CVRaw raw -> do v <- reduce =<< traverse evalConsts raw traverse reduce _tConstArg >>= \a -> typecheck [(a,v)] return $ Ref (TConst _tConstArg _tModule (CVEval raw $ liftTerm v) _tMeta _tInfo) - _ -> return $ Ref c + _ -> return rr _ -> Ref <$> traverse evalConsts r evalConsts r = return r @@ -698,14 +724,16 @@ appError i errDoc = TApp (App (msg errDoc) [] i) i resolveFreeVars :: Info -> Scope d Term Name -> Eval e (Scope d Term Ref) resolveFreeVars i b = traverse r b where - r fv = resolveRef fv >>= \m -> case m of + r fv = resolveRef i fv >>= \m -> case m of Nothing -> evalError i $ "Cannot resolve " <> pretty fv Just d -> return d -installModule :: ModuleData Ref -> Eval e () -installModule ModuleData{..} = do +-- | Install module into local namespace. If updated/new, update loaded modules. +installModule :: Bool -> ModuleData Ref -> Eval e () +installModule updated md@ModuleData{..} = do (evalRefs . rsLoaded) %= HM.union (HM.fromList . map (first (`Name` def)) . HM.toList $ _mdRefMap) - (evalRefs . rsLoadedModules) %= HM.insert (moduleDefName _mdModule) _mdModule + when updated $ + (evalRefs . rsLoadedModules) %= HM.insert (moduleDefName _mdModule) (md,updated) msg :: Doc -> Term n msg = toTerm . renderCompactText' @@ -823,9 +851,9 @@ runPure action = ask >>= \env -> case _eePurity env of PNoDb -> unsafeCoerce action -- yuck. would love safer coercion here _ -> mkNoDbEnv env >>= runPure' action -runReadOnly :: Info -> Eval (EnvReadOnly e) a -> Eval e a +runReadOnly :: HasInfo i => i -> Eval (EnvReadOnly e) a -> Eval e a runReadOnly i action = ask >>= \env -> case _eePurity env of - PNoDb -> evalError i "internal error: attempting sysread in pure context" + PNoDb -> evalError' i "internal error: attempting db read in pure context" PReadOnly -> unsafeCoerce action -- yuck. would love safer coercion here _ -> mkReadOnlyEnv env >>= runPure' action diff --git a/src/Pact/Native.hs b/src/Pact/Native.hs index a78fc114f..9e8f228e1 100644 --- a/src/Pact/Native.hs +++ b/src/Pact/Native.hs @@ -643,9 +643,9 @@ typeof'' _ [t] = return $ tStr $ typeof' t typeof'' i as = argsError i as listModules :: RNativeFun e -listModules _ _ = do - mods <- view $ eeRefStore.rsModules - return $ toTermList tTyString $ map asString $ HM.keys mods +listModules i _ = do + mods <- keys (_faInfo i) Modules + return $ toTermList tTyString $ map asString mods yield :: RNativeFun e yield i [t@(TObject (Object o _ _ _) _)] = do diff --git a/src/Pact/Native/Capabilities.hs b/src/Pact/Native/Capabilities.hs index 7831ed3ad..a7f81b66f 100644 --- a/src/Pact/Native/Capabilities.hs +++ b/src/Pact/Native/Capabilities.hs @@ -203,7 +203,7 @@ createUserGuard = createUserGuard' i [TObject udata _,TLitString predfun] = case parseName (_faInfo i) predfun of Right n -> do - rn <- resolveRef n >>= \nm -> case nm of + rn <- resolveRef i n >>= \nm -> case nm of Just (Direct {}) -> return n Just (Ref (TDef Def{..} _)) -> return $ QName _dModule (asString _dDefName) _dInfo diff --git a/src/Pact/Native/Db.hs b/src/Pact/Native/Db.hs index 1a743f9f2..faf77accf 100644 --- a/src/Pact/Native/Db.hs +++ b/src/Pact/Native/Db.hs @@ -26,7 +26,6 @@ import Control.Arrow hiding (app) import Control.Lens hiding ((.=)) import Control.Monad import Data.Default -import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.Map.Strict as M import qualified Data.Vector as V @@ -153,7 +152,7 @@ descKeySet i as = argsError i as descModule :: RNativeFun e descModule i [TLitString t] = do - mods <- view $ eeRefStore . rsModules . at (ModuleName t Nothing) + mods <- lookupModule i (ModuleName t Nothing) case _mdModule <$> mods of Just m -> case m of @@ -217,7 +216,8 @@ gasPostReads i g0 postProcess action = do columnsToObject :: Type (Term Name) -> ObjectMap PactValue -> Term Name columnsToObject ty m = TObject (Object (fmap fromPactValue m) ty def def) def -columnsToObject' :: Type (Term Name) -> [(Info,FieldKey)] -> ObjectMap PactValue -> Eval m (Term Name) +columnsToObject' :: Type (Term Name) -> [(Info,FieldKey)] -> + ObjectMap PactValue -> Eval m (Term Name) columnsToObject' ty cols (ObjectMap m) = do ps <- forM cols $ \(ci,col) -> case M.lookup col m of @@ -227,8 +227,6 @@ columnsToObject' ty cols (ObjectMap m) = do - - select :: NativeFun e select i as@[tbl',cols',app] = do cols <- reduce cols' >>= colsToList (argsError' i as) @@ -243,11 +241,14 @@ select' i _ cols' app@TApp{} tbl@TTable{} = do guardTable i tbl let fi = _faInfo i tblTy = _tTableType tbl - ks <- keys fi (userTable' tbl) - fmap (second (\b -> TList (V.fromList (reverse b)) tblTy def)) $ (\f -> foldM f (g0,[]) ks) $ \(gPrev,rs) k -> do + ks <- keys fi (userTable tbl) + fmap (second (\b -> TList (V.fromList (reverse b)) tblTy def)) $ + (\f -> foldM f (g0,[]) ks) $ \(gPrev,rs) k -> do + mrow <- readRow fi (userTable tbl) k case mrow of - Nothing -> evalError fi $ "select: unexpected error, key not found in select: " <> pretty k <> ", table: " <> pretty tbl + Nothing -> evalError fi $ "select: unexpected error, key not found in select: " + <> pretty k <> ", table: " <> pretty tbl Just row -> do g <- gasPostRead i gPrev row let obj = columnsToObject tblTy row @@ -258,7 +259,8 @@ select' i _ cols' app@TApp{} tbl@TTable{} = do Nothing -> return (obj:rs) Just cols -> (:rs) <$> columnsToObject' tblTy cols row | otherwise -> return rs - t -> evalError (_tInfo app) $ "select: filter returned non-boolean value: " <> pretty t + t -> evalError (_tInfo app) $ "select: filter returned non-boolean value: " + <> pretty t select' i as _ _ _ = argsError' i as @@ -298,7 +300,7 @@ keys' g i [table@TTable {..}] = gasPostReads i g ((\b -> TList (V.fromList b) tTyString def) . map toTerm) $ do guardTable i table - keys (_faInfo i) (userTable' table) + keys (_faInfo i) (userTable table) keys' _ i as = argsError i as @@ -367,13 +369,7 @@ guardTable i TTable {..} = guardForModuleCall (_faInfo i) _tModule $ guardTable i t = evalError' i $ "Internal error: guardTable called with non-table term: " <> pretty t enforceBlessedHashes :: FunApp -> ModuleName -> Hash -> Eval e () -enforceBlessedHashes i mn h = do - mmRs <- fmap _mdModule . HM.lookup mn <$> view (eeRefStore . rsModules) - mm <- maybe (HM.lookup mn <$> use (evalRefs.rsLoadedModules)) (return.Just) mmRs - case mm of - Nothing -> evalError' i $ "Internal error: Module " <> pretty mn <> " not found, could not enforce hashes" - Just m -> - case m of +enforceBlessedHashes i mn h = getModule i mn >>= \m -> case (_mdModule m) of MDModule Module{..} | h == _mHash -> return () -- current version ok | h `HS.member` _mBlessed -> return () -- hash is blessed diff --git a/src/Pact/Native/Internal.hs b/src/Pact/Native/Internal.hs index e2b17a67c..ac83e617a 100644 --- a/src/Pact/Native/Internal.hs +++ b/src/Pact/Native/Internal.hs @@ -34,22 +34,20 @@ module Pact.Native.Internal ,findCallingModule ) where -import Control.Monad -import Prelude -import Data.Default -import Pact.Eval -import Unsafe.Coerce +import Bound import Control.Lens hiding (Fold) +import Control.Monad import Data.Aeson hiding (Object) import qualified Data.Aeson.Lens as A -import Bound -import qualified Data.HashMap.Strict as HM -import Pact.Types.Pretty +import Data.Default import qualified Data.Vector as V +import Unsafe.Coerce -import Pact.Types.Runtime -import Pact.Types.Native +import Pact.Eval import Pact.Gas +import Pact.Types.Native +import Pact.Types.Pretty +import Pact.Types.Runtime success :: Functor m => Text -> m a -> m (Term Name) success = fmap . const . toTerm @@ -124,17 +122,11 @@ funType t as = funTypes $ funType' t as funType' :: Type n -> [(Text,Type n)] -> FunType n funType' t as = FunType (map (\(s,ty) -> Arg s ty def) as) t - -getModule :: Info -> ModuleName -> Eval e (ModuleDef (Def Ref)) -getModule i n = do - lm <- HM.lookup n <$> use (evalRefs.rsLoadedModules) - case lm of - Just m -> return m - Nothing -> do - rm <- HM.lookup n <$> view (eeRefStore.rsModules) - case rm of - Just ModuleData{..} -> return _mdModule - Nothing -> evalError i $ "Unable to resolve module " <> pretty n +-- | Lookup a module and fail if not found. +getModule :: HasInfo i => i -> ModuleName -> Eval e (ModuleData Ref) +getModule i mn = lookupModule i mn >>= \r -> case r of + Just m -> return m + Nothing -> evalError' i $ "Unable to resolve module " <> pretty mn tTyInteger :: Type n; tTyInteger = TyPrim TyInteger tTyDecimal :: Type n; tTyDecimal = TyPrim TyDecimal @@ -169,14 +161,14 @@ enforceGuardDef dn = enforceGuard :: FunApp -> Guard -> Eval e () enforceGuard i g = case g of - GKeySet k -> runPure $ enforceKeySet (_faInfo i) Nothing k + GKeySet k -> runReadOnly i $ enforceKeySet (_faInfo i) Nothing k GKeySetRef n -> enforceKeySetName (_faInfo i) n GPact PactGuard{..} -> do pid <- getPactId i unless (pid == _pgPactId) $ evalError' i $ "Pact guard failed, intended: " <> pretty _pgPactId <> ", active: " <> pretty pid GModule mg@ModuleGuard{..} -> do - m <- getModule (_faInfo i) _mgModuleName + m <- _mdModule <$> getModule (_faInfo i) _mgModuleName case m of MDModule Module{..} -> enforceModuleAdmin (_faInfo i) _mGovernance MDInterface{} -> evalError' i $ "ModuleGuard not allowed on interface: " <> pretty mg @@ -192,7 +184,7 @@ guardForModuleCall :: Info -> ModuleName -> Eval e () -> Eval e () guardForModuleCall i modName onFound = findCallingModule >>= \r -> case r of (Just mn) | mn == modName -> onFound _ -> do - md <- getModule i modName + md <- _mdModule <$> getModule i modName case md of MDModule m -> void $ acquireModuleAdmin i (_mName m) (_mGovernance m) MDInterface iface -> evalError i $ diff --git a/src/Pact/Native/Keysets.hs b/src/Pact/Native/Keysets.hs index 571082042..13eba05d8 100644 --- a/src/Pact/Native/Keysets.hs +++ b/src/Pact/Native/Keysets.hs @@ -72,7 +72,7 @@ defineKeyset fi as = case as of case old of Nothing -> writeRow i Write KeySets ksn ks & success "Keyset defined" Just oldKs -> do - runPure $ enforceKeySet i (Just ksn) oldKs + runReadOnly i $ enforceKeySet i (Just ksn) oldKs writeRow i Write KeySets ksn ks & success "Keyset defined" keyPred :: (Integer -> Integer -> Bool) -> RNativeFun e diff --git a/src/Pact/PersistPactDb.hs b/src/Pact/PersistPactDb.hs index eb60f2a1c..9362a52a1 100644 --- a/src/Pact/PersistPactDb.hs +++ b/src/Pact/PersistPactDb.hs @@ -35,6 +35,7 @@ import Control.Monad import Control.Monad.Catch import Control.Monad.State.Strict import Data.Typeable +import Data.String import Data.Aeson hiding ((.=)) import GHC.Generics @@ -115,6 +116,11 @@ doPersist :: (Persister p -> Persist p a) -> MVState p a doPersist f = get >>= \m -> liftIO (f (_persist m) (_db m)) >>= \(db',r) -> db .= db' >> return r {-# INLINE doPersist #-} +toTableId :: Domain k v -> TableId +toTableId KeySets = keysetsTable +toTableId Modules = modulesTable +toTableId Namespaces = namespacesTable +toTableId (UserTables t) = userTable t pactdb :: PactDb (DbEnv p) pactdb = PactDb @@ -133,7 +139,7 @@ pactdb = PactDb (UserTables t) -> writeUser e wt t k v , _keys = \tn e -> runMVState e - (map (RowKey . asString) <$> doPersist (\p -> queryKeys p (userDataTable tn) Nothing)) + (map (fromString . unpack . asString) <$> doPersist (\p -> queryKeys p (DataTable $ toTableId tn) Nothing)) , _txids = \tn tid e -> runMVState e diff --git a/src/Pact/Repl.hs b/src/Pact/Repl.hs index 8c6086148..016d780b7 100644 --- a/src/Pact/Repl.hs +++ b/src/Pact/Repl.hs @@ -39,6 +39,9 @@ module Pact.Repl , setReplLib , unsetReplLib , utf8BytesLength + , evalReplEval + , replGetModules + , replLookupModule ) where import Control.Applicative @@ -103,10 +106,9 @@ initReplState m verifyUri = initPureEvalEnv :: Maybe String -> IO (EvalEnv LibState) initPureEvalEnv verifyUri = do mv <- initLibState neverLog verifyUri >>= newMVar - return $ EvalEnv (RefStore nativeDefs mempty) def Null (Just 0) + return $ EvalEnv (RefStore nativeDefs) def Null (Just 0) def def mv repldb def pactInitialHash freeGasEnv permissiveNamespacePolicy (SPVSupport $ spv mv) def - spv :: MVar (LibState) -> Text -> Object Name -> IO (Either Text (Object Name)) spv mv ty pay = readMVar mv >>= \LibState{..} -> case M.lookup (SPVMockKey (ty,pay)) _rlsMockSPV of Nothing -> return $ Left $ "SPV verification failure" @@ -184,11 +186,7 @@ compileEval src exp = handleCompile src exp $ \e -> pureEval (_tInfo e) (eval e) pureEval :: Info -> Eval LibState (Term Name) -> Repl (Either String (Term Name)) pureEval ei e = do - (ReplState evalE evalS _ _ _ _) <- get - er <- try (liftIO $ runEval' evalS evalE e) - let (r,es) = case er of - Left (SomeException ex) -> (Left (PactError EvalError def def (prettyString (show ex))),evalS) - Right v -> v + (r,es) <- evalEval ei e mode <- use rMode case r of Right a -> do @@ -219,6 +217,14 @@ pureEval ei e = do mapM_ (\c -> outStrLn HErr $ " at " ++ show c) cs return (Left serr) +evalEval :: Info -> Eval LibState a -> Repl (Either PactError a, EvalState) +evalEval ei e = do + (ReplState evalE evalS _ _ _ _) <- get + er <- try (liftIO $ runEval' evalS evalE e) + return $ case er of + Left (SomeException ex) -> (Left (PactError EvalError ei def (prettyString (show ex))),evalS) + Right v -> v + doOut :: Info -> ReplMode -> Term Name -> Repl () doOut ei mode a = case mode of Interactive -> plainOut @@ -274,11 +280,6 @@ doTx i t n = do Rollback -> return $ evalRollbackTx i Commit -> return $ void $ evalCommitTx i pureEval i (e >> return (tStr "")) >>= \r -> forM r $ \_ -> do - case t of - Commit -> do - newmods <- use (rEvalState . evalRefs . rsNewModules) - rEnv . eeRefStore . rsModules %= HM.union newmods - _ -> return () rEvalState .= def useReplLib tid <- use $ rEnv . eeTxId @@ -356,6 +357,27 @@ execScript' m fp = do s <- initReplState m Nothing runStateT (useReplLib >> loadFile fp) s +evalReplEval :: Info -> ReplState -> Eval LibState a -> IO (Either PactError (a, ReplState)) +evalReplEval i rs e = do + ((r,es),rs') <- runStateT (evalEval i e) rs + case r of + Left err -> return $ Left err + Right a -> return $ Right (a, set rEvalState es rs') + +replGetModules :: ReplState -> + IO (Either PactError + (HM.HashMap ModuleName (ModuleData Ref), ReplState)) +replGetModules rs = evalReplEval def rs (getAllModules (def :: Info)) + +replLookupModule :: ReplState -> ModuleName -> IO (Either String (ModuleData Ref)) +replLookupModule rs mn = do + modulesM <- replGetModules rs + pure $ case modulesM of + Left err -> Left $ show err + Right (modules,_) -> + case HM.lookup mn modules of + Nothing -> Left $ "module not found: " ++ show mn ++ ", modules=" ++ show (HM.keys modules) + Just moduleData -> Right moduleData -- | install repl lib functions into monad state useReplLib :: Repl () diff --git a/src/Pact/Repl/Lib.hs b/src/Pact/Repl/Lib.hs index 5dfd25bca..30194b241 100644 --- a/src/Pact/Repl/Lib.hs +++ b/src/Pact/Repl/Lib.hs @@ -29,7 +29,6 @@ import Control.Monad.Catch import Data.Aeson (eitherDecode,toJSON) import qualified Data.ByteString.Lazy as BSL import Data.Default -import qualified Data.HashMap.Strict as HM import qualified Data.Map as M import Data.Semigroup (Endo(..)) import qualified Data.Set as S @@ -420,38 +419,33 @@ tc i as = case as of _ -> argsError i as where go modname dbg = do - mdm <- HM.lookup (ModuleName modname Nothing) <$> view (eeRefStore . rsModules) - case mdm of - Nothing -> evalError' i $ "No such module: " <> pretty modname - Just md -> do - r :: Either TC.CheckerException ([TC.TopLevel TC.Node],[TC.Failure]) <- - try $ liftIO $ typecheckModule dbg md - case r of - Left (TC.CheckerException ei e) -> evalError ei ("Typechecker Internal Error: " <> prettyString e) - Right (_,fails) -> case fails of - [] -> return $ tStr $ "Typecheck " <> modname <> ": success" - _ -> do - setop $ TcErrors $ map (\(TC.Failure ti s) -> renderInfo (TC._tiInfo ti) ++ ":Warning: " ++ s) fails - return $ tStr $ "Typecheck " <> modname <> ": Unable to resolve all types" + md <- getModule i (ModuleName modname Nothing) + r :: Either TC.CheckerException ([TC.TopLevel TC.Node],[TC.Failure]) <- + try $ liftIO $ typecheckModule dbg md + case r of + Left (TC.CheckerException ei e) -> evalError ei ("Typechecker Internal Error: " <> prettyString e) + Right (_,fails) -> case fails of + [] -> return $ tStr $ "Typecheck " <> modname <> ": success" + _ -> do + setop $ TcErrors $ map (\(TC.Failure ti s) -> renderInfo (TC._tiInfo ti) ++ ":Warning: " ++ s) fails + return $ tStr $ "Typecheck " <> modname <> ": Unable to resolve all types" verify :: RNativeFun LibState verify i as = case as of [TLitString modName] -> do - modules <- view (eeRefStore . rsModules) - let mdm = HM.lookup (ModuleName modName Nothing) modules - case mdm of - Nothing -> evalError' i $ "No such module: " <> pretty modName - Just md -> do + md <- getModule i (ModuleName modName Nothing) + -- reading all modules from db here, but should be fine in repl + modules <- getAllModules i #if defined(ghcjs_HOST_OS) - uri <- fromMaybe "localhost" <$> viewLibState (view rlsVerifyUri) - renderedLines <- liftIO $ - RemoteClient.verifyModule modules md uri + uri <- fromMaybe "localhost" <$> viewLibState (view rlsVerifyUri) + renderedLines <- liftIO $ + RemoteClient.verifyModule modules md uri #else - modResult <- liftIO $ Check.verifyModule modules md - let renderedLines = Check.renderVerifiedModule modResult + modResult <- liftIO $ Check.verifyModule modules md + let renderedLines = Check.renderVerifiedModule modResult #endif - setop $ TcErrors $ Text.unpack <$> renderedLines - return (tStr $ mconcat renderedLines) + setop $ TcErrors $ Text.unpack <$> renderedLines + return (tStr $ mconcat renderedLines) _ -> argsError i as diff --git a/src/Pact/Repl/Types.hs b/src/Pact/Repl/Types.hs index 7d928df1c..cd34ac911 100644 --- a/src/Pact/Repl/Types.hs +++ b/src/Pact/Repl/Types.hs @@ -9,19 +9,23 @@ module Pact.Repl.Types , LibState(..),rlsPure,rlsOp,rlsTxName,rlsTests,rlsVerifyUri,rlsMockSPV,rlsPacts , Tx(..) , SPVMockKey(..) + , getAllModules ) where import Control.Lens (makeLenses) +import Control.Monad import Data.Default (Default(..)) import Data.Monoid (Endo(..)) import Control.Monad.State.Strict (StateT) import Control.Concurrent (MVar) import Pact.PersistPactDb (DbEnv) import Pact.Persist.Pure (PureDb) -import Pact.Types.Runtime (EvalEnv,EvalState,Term,Name,FunApp,Info,Object,Term(..),PactId,PactExec) +import Pact.Types.Runtime import Data.Text (Text) import qualified Data.Map.Strict as M +import qualified Data.HashMap.Strict as HM import Pact.Types.Pretty (Pretty,pretty,renderCompactText) +import Pact.Native.Internal data ReplMode = Interactive | @@ -83,3 +87,10 @@ data LibState = LibState { makeLenses ''LibState makeLenses ''ReplState + +getAllModules :: HasInfo i => i -> Eval e (HM.HashMap ModuleName (ModuleData Ref)) +getAllModules i = do + mks <- keys (getInfo i) Modules + fmap HM.fromList $ forM mks $ \mk -> do + m <- getModule i mk + return (mk,m) diff --git a/src/Pact/Types/Persistence.hs b/src/Pact/Types/Persistence.hs index b7fa5ff1d..881e952e0 100644 --- a/src/Pact/Types/Persistence.hs +++ b/src/Pact/Types/Persistence.hs @@ -196,8 +196,8 @@ data PactDb e = PactDb { -- | Write a domain value at key. WriteType argument governs key behavior. , _writeRow :: forall k v . (AsString k,ToJSON v) => WriteType -> Domain k v -> k -> v -> Method e () - -- | Retrieve all keys for user table. - , _keys :: TableName -> Method e [RowKey] + -- | Retrieve all keys for a domain. + , _keys :: forall k v . (IsString k,AsString k) => Domain k v -> Method e [k] -- | Retrieve all transaction ids greater than supplied txid for table. , _txids :: TableName -> TxId -> Method e [TxId] -- | Create a user table. diff --git a/src/Pact/Types/Runtime.hs b/src/Pact/Types/Runtime.hs index 6130d8740..80c7f19dd 100644 --- a/src/Pact/Types/Runtime.hs +++ b/src/Pact/Types/Runtime.hs @@ -20,13 +20,13 @@ module Pact.Types.Runtime evalError,evalError',failTx,argsError,argsError',throwDbError,throwEither,throwErr, PactId(..), PactStep(..),psStep,psRollback,psPactId,psResume, - RefStore(..),rsNatives,rsModules,updateRefStore, + RefStore(..),rsNatives, EvalEnv(..),eeRefStore,eeMsgSigs,eeMsgBody,eeTxId,eeEntity,eePactStep,eePactDbVar, eePactDb,eePurity,eeHash,eeGasEnv,eeNamespacePolicy,eeSPVSupport,eePublicData, - Purity(..),PureNoDb,PureSysRead,EnvNoDb(..),EnvReadOnly(..),mkNoDbEnv,mkReadOnlyEnv, + Purity(..),PureNoDb,PureReadOnly,EnvNoDb(..),EnvReadOnly(..),mkNoDbEnv,mkReadOnlyEnv, StackFrame(..),sfName,sfLoc,sfApp, PactExec(..),peStepCount,peYield,peExecuted,pePactId,peStep,peContinuation, - RefState(..),rsLoaded,rsLoadedModules,rsNewModules,rsNamespace, + RefState(..),rsLoaded,rsLoadedModules,rsNamespace, EvalState(..),evalRefs,evalCallStack,evalPactExec,evalGas,evalCapabilities, Eval(..),runEval,runEval', call,method, @@ -149,13 +149,12 @@ makeLenses ''PactStep --- | Storage for loaded modules, interfaces, and natives. +-- | Storage for natives. data RefStore = RefStore { _rsNatives :: HM.HashMap Name Ref - , _rsModules :: HM.HashMap ModuleName (ModuleData Ref) } deriving (Eq, Show) makeLenses ''RefStore -instance Default RefStore where def = RefStore HM.empty HM.empty +instance Default RefStore where def = RefStore HM.empty data PactContinuation = PactContinuation { _pcDef :: Def Ref @@ -194,7 +193,7 @@ instance Default Purity where def = PImpure class PureNoDb e -- | Marker class for 'PReadOnly' environments. -- SysRead supports pure operations as well. -class PureNoDb e => PureSysRead e +class PureNoDb e => PureReadOnly e -- | Backend for SPV newtype SPVSupport = SPVSupport { @@ -242,25 +241,17 @@ makeLenses ''EvalEnv --- | Dynamic storage for namespace-loaded modules, and new modules compiled in current tx. +-- | Dynamic storage for loaded names and modules, and current namespace. data RefState = RefState { -- | Imported Module-local defs and natives. _rsLoaded :: HM.HashMap Name Ref - -- | Modules that were loaded. - , _rsLoadedModules :: HM.HashMap ModuleName (ModuleDef (Def Ref)) - -- | Modules that were compiled and loaded in this tx. - , _rsNewModules :: HM.HashMap ModuleName (ModuleData Ref) + -- | Modules that were loaded, and flag if updated. + , _rsLoadedModules :: HM.HashMap ModuleName (ModuleData Ref, Bool) -- | Current Namespace , _rsNamespace :: Maybe Namespace } deriving (Eq,Show) makeLenses ''RefState -instance Default RefState where def = RefState HM.empty HM.empty HM.empty Nothing - --- | Update for newly-loaded modules and interfaces. -updateRefStore :: RefState -> RefStore -> RefStore -updateRefStore RefState {..} - | HM.null _rsNewModules = id - | otherwise = over rsModules (HM.union _rsNewModules) +instance Default RefState where def = RefState HM.empty HM.empty Nothing data Capabilities = Capabilities { _capGranted :: [Capability] @@ -337,7 +328,7 @@ writeRow :: (AsString k,ToJSON v) => Info -> WriteType -> Domain k v -> k -> v - writeRow i w d k v = method i $ \db -> _writeRow db w d k v -- | Invoke '_keys' -keys :: Info -> TableName -> Eval e [RowKey] +keys :: (AsString k,IsString k) => Info -> Domain k v -> Eval e [k] keys i t = method i $ \db -> _keys db t -- | Invoke '_txids' @@ -424,7 +415,7 @@ instance PureNoDb (EnvNoDb e) newtype EnvReadOnly e = EnvReadOnly (EvalEnv e) -instance PureSysRead (EnvReadOnly e) +instance PureReadOnly (EnvReadOnly e) instance PureNoDb (EnvReadOnly e) diePure :: Method e a diff --git a/src/Pact/Types/Term.hs b/src/Pact/Types/Term.hs index c7c54a5c5..296916acc 100644 --- a/src/Pact/Types/Term.hs +++ b/src/Pact/Types/Term.hs @@ -452,6 +452,10 @@ data Name | Name { _nName :: Text, _nInfo :: Info } deriving (Generic, Show) +instance HasInfo Name where + getInfo (QName _ _ i) = i + getInfo (Name _ i) = i + instance Pretty Name where pretty = \case QName modName nName _ -> pretty modName <> "." <> pretty nName @@ -688,10 +692,10 @@ instance ToJSON n => ToJSON (ConstVal n) where instance FromJSON n => FromJSON (ConstVal n) where parseJSON v = - (withObject "CVRaw" - (\o -> CVRaw <$> o .: "raw") v) <|> (withObject "CVEval" - (\o -> CVEval <$> o .: "raw" <*> o .: "eval") v) + (\o -> CVEval <$> o .: "raw" <*> o .: "eval") v) <|> + (withObject "CVRaw" + (\o -> CVRaw <$> o .: "raw") v) data Example diff --git a/tests/AnalyzeSpec.hs b/tests/AnalyzeSpec.hs index 3969ec3b8..b8e388ab4 100644 --- a/tests/AnalyzeSpec.hs +++ b/tests/AnalyzeSpec.hs @@ -11,8 +11,8 @@ module AnalyzeSpec (spec) where -import Control.Lens (at, findOf, ix, matching, (&), - (.~), (^.), (^..), _Left) +import Control.Lens (findOf, ix, matching, (&), + (.~), (^..), _Left) import Control.Monad (unless) import Control.Monad.Except (runExceptT) import Control.Monad.State.Strict (runStateT) @@ -35,10 +35,9 @@ import Test.Hspec (Spec, describe, import qualified Test.HUnit as HUnit import Pact.Parse (parseExprs) -import Pact.Repl (evalRepl', initReplState) -import Pact.Repl.Types (ReplMode (StringEval), rEnv) -import Pact.Types.Runtime (Exp, Info, ModuleData, Ref, - eeRefStore, rsModules) +import Pact.Repl (evalRepl', initReplState, replLookupModule) +import Pact.Repl.Types (ReplMode (StringEval)) +import Pact.Types.Runtime (Exp, Info, ModuleData, Ref) import Pact.Types.Pretty (Pretty, renderCompactString) import Pact.Types.Util (tShow) @@ -124,13 +123,11 @@ renderTestFailure = \case compile :: Text -> IO (Either TestFailure (ModuleData Ref)) compile code = do replState0 <- initReplState StringEval Nothing - (eTerm, replState) <- runStateT (evalRepl' $ T.unpack code) replState0 - pure $ case eTerm of - Left err -> Left $ ReplError err - Right _t -> - case replState ^. rEnv . eeRefStore . rsModules . at "test" of - Nothing -> Left NoTestModule - Just moduleData -> Right moduleData + (_, replState) <- runStateT (evalRepl' $ T.unpack code) replState0 + moduleM <- replLookupModule replState "test" + pure $ case moduleM of + Left err -> Left $ ReplError (show err) + Right m -> Right m runVerification :: Text -> IO (Maybe TestFailure) runVerification code = do diff --git a/tests/RemoteVerifySpec.hs b/tests/RemoteVerifySpec.hs index 2f5c08e79..119380b3a 100644 --- a/tests/RemoteVerifySpec.hs +++ b/tests/RemoteVerifySpec.hs @@ -9,7 +9,7 @@ import Control.Concurrent import Control.Exception (finally) import Control.Lens import Control.Monad.State.Strict -import Data.Maybe +import Data.Either import qualified Data.Text as T import Test.Hspec import NeatInterpolation (text) @@ -40,8 +40,8 @@ loadCode code = do Left err -> Left $ ReplError err Right _t -> Right replState -stateModuleData :: ModuleName -> ReplState -> Maybe (ModuleData Ref) -stateModuleData nm replState = replState ^. rEnv . eeRefStore . rsModules . at nm +stateModuleData :: ModuleName -> ReplState -> IO (Either String (ModuleData Ref)) +stateModuleData nm replState = replLookupModule replState nm serve :: Int -> IO ThreadId serve port = forkIO $ runServantServer port @@ -59,7 +59,7 @@ serveAndRequest port body = do testSingleModule :: Spec testSingleModule = do - eReplState0 <- runIO $ loadCode + replState0 <- runIO $ either (error.show) id <$> loadCode [text| (env-keys ["admin"]) (env-data { "keyset": { "keys": ["admin"], "pred": "=" } }) @@ -76,16 +76,15 @@ testSingleModule = do |] it "loads locally" $ do - Right replState0 <- pure eReplState0 - stateModuleData "mod1" replState0 `shouldSatisfy` isJust + stateModuleData "mod1" replState0 >>= (`shouldSatisfy` isRight) - Right replState0 <- pure eReplState0 - Just (ModuleData mod1 _refs) <- pure $ stateModuleData "mod1" replState0 + (ModuleData mod1 _refs) <- runIO $ either error id <$> stateModuleData "mod1" replState0 resp <- runIO $ serveAndRequest 3000 $ Remote.Request [derefDef <$> mod1] "mod1" it "verifies over the network" $ - (Right ["Property proven valid",""]) `shouldBe` fmap (view Remote.responseLines) resp + fmap (view Remote.responseLines) resp `shouldBe` + (Right ["Property proven valid",""]) testUnsortedModules :: Spec testUnsortedModules = do @@ -114,13 +113,14 @@ testUnsortedModules = do it "loads when topologically sorted locally" $ do Right replState0 <- pure eReplState0 - stateModuleData "mod2" replState0 `shouldSatisfy` isJust + stateModuleData "mod2" replState0 >>= (`shouldSatisfy` isRight) Right replState0 <- pure eReplState0 - Just (ModuleData mod1 _refs) <- pure $ stateModuleData "mod1" replState0 - Just (ModuleData mod2 _refs) <- pure $ stateModuleData "mod2" replState0 + Right (ModuleData mod1 _refs) <- runIO $ stateModuleData "mod1" replState0 + Right (ModuleData mod2 _refs) <- runIO $ stateModuleData "mod2" replState0 resp <- runIO $ serveAndRequest 3001 $ Remote.Request [derefDef <$> mod2, derefDef <$> mod1] "mod2" it "verifies over the network" $ - (Right ["Property proven valid",""]) `shouldBe` fmap (view Remote.responseLines) resp + fmap (view Remote.responseLines) resp `shouldBe` + (Right ["Property proven valid",""]) diff --git a/tests/SignatureSpec.hs b/tests/SignatureSpec.hs index d39428239..73bd5d9d2 100644 --- a/tests/SignatureSpec.hs +++ b/tests/SignatureSpec.hs @@ -5,17 +5,15 @@ module SignatureSpec (spec) where import Test.Hspec import Control.Monad (forM_) -import Control.Lens (preview, ix) import Data.Default (def) -import qualified Data.HashMap.Lazy as HM +import qualified Data.HashMap.Strict as HM + import Pact.Repl import Pact.Repl.Types -import Pact.Typechecker (die) import Pact.Types.Exp import Pact.Types.Info (Info(..)) -import Pact.Types.Runtime (RefStore(..), ModuleData(..), - eeRefStore, rsModules) +import Pact.Types.Runtime import Pact.Types.Term (Module(..), Interface(..), ModuleName(..), ModuleDef(..), Meta(..), Term(..), Ref'(..), Ref, Def(..)) @@ -25,9 +23,13 @@ spec = compareModelSpec compareModelSpec :: Spec compareModelSpec = describe "Module models" $ do - rs <- runIO $ loadRefStore "tests/pact/signatures.repl" - md <- runIO $ loadModuleData rs (ModuleName "model-test1-impl" Nothing) - ifd <- runIO $ loadModuleData rs (ModuleName "model-test1" Nothing) + (r,s) <- runIO $ execScript' Quiet "tests/pact/signatures.repl" + case r of + Left e -> it "loaded script" $ expectationFailure e + Right _ -> return () + Right (rs,_) <- runIO $ replGetModules s + Just md <- return $ HM.lookup (ModuleName "model-test1-impl" Nothing) rs + Just ifd <- return $ HM.lookup (ModuleName "model-test1" Nothing) rs let mModels = case _mdModule md of MDModule m -> _mModel $ _mMeta m @@ -52,7 +54,7 @@ aggregateFunctionModels :: ModuleData Ref -> [Exp Info] aggregateFunctionModels ModuleData{..} = foldMap (extractExp . snd) $ HM.toList _mdRefMap where - extractExp (Ref (TDef (Def _ _ _ _ _ Meta{_mModel=mModel} _) _)) = mModel + extractExp (Ref (TDef (Def _ _ _ _ _ Meta{_mModel=m} _) _)) = m extractExp _ = [] -- Because models will necessarily have conflicting Info values @@ -60,17 +62,3 @@ aggregateFunctionModels ModuleData{..} = -- 'Info', and only compares relevant terms. expEquality :: Exp Info -> Exp Info -> Bool expEquality e1 e2 = ((def :: Info) <$ e1) == ((def :: Info) <$ e2) - -loadRefStore :: FilePath -> IO RefStore -loadRefStore fp = do - (r,s) <- execScript' Quiet fp - either (die def) (const (return ())) r - case preview (rEnv . eeRefStore) s of - Just md -> return md - Nothing -> die def $ "Could not load module data from " ++ show fp - -loadModuleData :: RefStore -> ModuleName -> IO (ModuleData Ref) -loadModuleData rs mn = - case preview (rsModules . ix mn) rs of - Just md -> pure md - Nothing -> die def $ "Could not load module data: " ++ show mn diff --git a/tests/TypecheckSpec.hs b/tests/TypecheckSpec.hs index 754ac5d9d..8aa7427ff 100644 --- a/tests/TypecheckSpec.hs +++ b/tests/TypecheckSpec.hs @@ -97,9 +97,9 @@ loadModule :: FilePath -> ModuleName -> IO (ModuleData Ref) loadModule fp mn = do (r,s) <- execScript' Quiet fp either (die def) (const (return ())) r - case view (rEnv . eeRefStore . rsModules . at mn) s of - Just m -> return m - Nothing -> die def $ "Module not found: " ++ show (fp,mn) + replLookupModule s mn >>= \mr -> case mr of + Right m -> return m + Left e -> die def $ "Module not found: " ++ show (fp,mn,e) loadFun :: FilePath -> ModuleName -> Text -> IO Ref loadFun fp mn fn = loadModule fp mn >>= \(ModuleData _ m) -> case HM.lookup fn m of