From 081e059102325596605c1d33a9ec43ff19932b93 Mon Sep 17 00:00:00 2001 From: jmcardon Date: Tue, 27 Aug 2024 15:09:17 -0400 Subject: [PATCH] Core: Add gas to post-reads, make module admin acquisition explicit. --- docs/builtins/General/acquire-module-admin.md | 38 ++++ docs/builtins/Operators/eq.md | 6 +- pact-tests/Pact/Core/Test/PersistenceTests.hs | 8 +- pact-tests/Pact/Core/Test/StaticErrorTests.hs | 8 +- .../CoreBuiltin.golden | 7 +- .../constructor-tag-goldens/EvalError.golden | 3 +- .../gas-goldens/acquire-module-admin.repl | 10 + pact-tests/gas-goldens/builtinGas.golden | 7 +- .../legacy-serial-tests/coin-v5/coin-v5.repl | 6 +- .../marmalade-v2/marmalade.repl | 3 +- pact-tests/pact-tests/coin-v1.repl | 6 +- pact-tests/pact-tests/coin-v5.repl | 6 +- pact-tests/pact-tests/db.repl | 4 + pact-tests/pact-tests/gov.repl | 6 +- pact-tests/pact-tests/leftpad.repl | 2 + pact-tests/pact-tests/side-effects.repl | 27 ++- pact-tests/pact-tests/try.repl | 1 + pact-tng.cabal | 3 + pact/Pact/Core/Builtin.hs | 4 + pact/Pact/Core/Compile.hs | 16 +- pact/Pact/Core/Environment/Utils.hs | 76 ------- pact/Pact/Core/Errors.hs | 4 + pact/Pact/Core/Evaluate.hs | 15 +- pact/Pact/Core/Gas/TableGasModel.hs | 1 + pact/Pact/Core/IR/Desugar.hs | 32 ++- pact/Pact/Core/IR/Eval/CEK.hs | 195 ++++++++---------- pact/Pact/Core/IR/Eval/CEK/Types.hs | 36 +--- pact/Pact/Core/IR/Eval/CEK/Utils.hs | 4 +- pact/Pact/Core/IR/Eval/CoreBuiltin.hs | 126 +++++++---- pact/Pact/Core/IR/Eval/Direct/Evaluator.hs | 163 +++++++-------- pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs | 3 +- pact/Pact/Core/IR/Eval/Direct/Types.hs | 5 - pact/Pact/Core/IR/Eval/Runtime/Types.hs | 8 +- pact/Pact/Core/IR/Eval/Runtime/Utils.hs | 52 ++++- pact/Pact/Core/Interpreter.hs | 2 + pact/Pact/Core/Legacy/LegacyPactValue.hs | 81 ++++++-- pact/Pact/Core/Persistence/MockPersistence.hs | 25 ++- pact/Pact/Core/Persistence/SQLite.hs | 27 ++- pact/Pact/Core/Persistence/Types.hs | 9 +- pact/Pact/Core/Persistence/Utils.hs | 81 ++++++++ pact/Pact/Core/Repl/Compile.hs | 15 +- pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs | 3 +- pact/Pact/Core/Serialise.hs | 3 +- pact/Pact/Core/Serialise/LegacyPact/Types.hs | 1 + profile-tx/ProfileTx.hs | 28 +-- test-utils/Pact/Core/Gen.hs | 37 ++-- test-utils/Pact/Core/PactDbRegression.hs | 18 +- 47 files changed, 705 insertions(+), 516 deletions(-) create mode 100644 docs/builtins/General/acquire-module-admin.md create mode 100644 pact-tests/gas-goldens/acquire-module-admin.repl diff --git a/docs/builtins/General/acquire-module-admin.md b/docs/builtins/General/acquire-module-admin.md new file mode 100644 index 000000000..722a1f5db --- /dev/null +++ b/docs/builtins/General/acquire-module-admin.md @@ -0,0 +1,38 @@ +## acquire-module-admin + +Use `acquire-module-admin` to grant module admin privileges for a particular module `m`. You must already own admin for this particular module; that is, you must either be the owner of the keyset that grants the governance, or be able to pass the governance capability acquisition. + +### Basic syntax + +Assume you have some module +```pact +(module my-module gov + (defcap gov () + (do-stuff-for-governance) + ) +) +``` + +To grant module admin, use + +```pact +(acquire-module some-module) +``` + +It will attempt to acquire the governance cap and if successful, it will grant module admin for the rest of the transaction. + +### Arguments + +Use one of the following argument to define the value you want to retrieve using the `at` Pact function. + +| Argument | Type | Description +| -------- | ---- | ----------- +| `ref` | modref | Specifies the module to acquire administrative capabilities for + +### Return values + +Module admin acquisition will either fail, or return "Module admin for module acquired" + +### Examples + +See: Basic Syntax. diff --git a/docs/builtins/Operators/eq.md b/docs/builtins/Operators/eq.md index ea21f6c2e..267431b60 100644 --- a/docs/builtins/Operators/eq.md +++ b/docs/builtins/Operators/eq.md @@ -14,8 +14,8 @@ Use the following arguments to specify the values for comparison using the `=` P | Argument | Type | Description | | --- | --- | --- | -| `oper1` | integer, decimal, string, time, bool, object, list, or table | Specifies the first value for comparison. | -| `oper2` | integer, decimal, string, time, bool, object, list, table | Specifies the second value for comparison. | +| `oper1` | integer, decimal, string, time, bool, object, list, modref, guard | Specifies the first value for comparison. | +| `oper2` | integer, decimal, string, time, bool, object, list, modref, guard | Specifies the second value for comparison. | ### Return value @@ -70,4 +70,4 @@ For example: ```pact (enforce (= amount 1.0) "Mint can only be 1") -``` \ No newline at end of file +``` diff --git a/pact-tests/Pact/Core/Test/PersistenceTests.hs b/pact-tests/Pact/Core/Test/PersistenceTests.hs index ae17c4f93..1a0483426 100644 --- a/pact-tests/Pact/Core/Test/PersistenceTests.hs +++ b/pact-tests/Pact/Core/Test/PersistenceTests.hs @@ -62,7 +62,7 @@ keysetPersistRoundtrip serial builtins keysetGen = evalEnv <- liftIO $ getEvalEnv serial builtins writtenKeySetResult <- liftIO $ runEvalM (ExecEnv evalEnv) def $ withSqlitePactDb serial ":memory:" $ \db -> do evalWrite def db Insert DKeySets keysetName keyset - liftIO $ _pdbRead db DKeySets keysetName + liftGasM def $ _pdbRead db DKeySets keysetName case writtenKeySetResult of (Left _, _) -> fail "Unexpected EvalM error" (Right writtenKeySet, _) -> Just keyset === writtenKeySet @@ -74,7 +74,7 @@ moduleDataRoundtrip serial builtins b i = property $ do evalEnv <- liftIO $ getEvalEnv serial builtins readResult <- liftIO $ runEvalM (ExecEnv evalEnv) def $ withSqlitePactDb serial ":memory:" $ \db -> do () <- evalWrite def db Insert DModules moduleName moduleData - liftIO $ _pdbRead db DModules moduleName + liftGasM def $ _pdbRead db DModules moduleName case readResult of (Left _, _) -> fail "Unexpected EvalM error" (Right writtenModuleData, _) -> @@ -87,7 +87,7 @@ defPactExecRoundtrip serial builtins _b _i = property $ do evalEnv <- liftIO $ getEvalEnv serial builtins writeResult <- liftIO $ runEvalM (ExecEnv evalEnv) def $ withSqlitePactDb serial ":memory:" $ \db -> do () <- evalWrite def db Insert DDefPacts defPactId defPactExec - liftIO $ _pdbRead db DDefPacts defPactId + liftGasM def $ _pdbRead db DDefPacts defPactId case writeResult of (Left _, _) -> fail "Unexpected EvalM error" (Right writtenDefPactExec, _) -> @@ -100,7 +100,7 @@ namespaceRoundtrip serial builtins = property $ do evalEnv <- liftIO $ getEvalEnv serial builtins writeResult <- liftIO $ runEvalM (ExecEnv evalEnv) def $ withSqlitePactDb serial ":memory:" $ \db -> do () <- evalWrite def db Insert DNamespaces ns namespace - liftIO $ _pdbRead db DNamespaces ns + liftGasM def $ _pdbRead db DNamespaces ns case writeResult of (Left _, _) -> fail "Unexpected EvalM error" (Right writtenNamespace, _) -> diff --git a/pact-tests/Pact/Core/Test/StaticErrorTests.hs b/pact-tests/Pact/Core/Test/StaticErrorTests.hs index 0019cc792..f6483622c 100644 --- a/pact-tests/Pact/Core/Test/StaticErrorTests.hs +++ b/pact-tests/Pact/Core/Test/StaticErrorTests.hs @@ -676,13 +676,7 @@ executionTests = ) |]) - -- CEK errors - , ("modref_no_ns", isExecutionError _ModRefImplementsNoInterfaces, [text| - (module m g (defcap g () true)) - m - |]) - - , ("defpact_not_init", isExecutionError _NoDefPactIdAndExecEnvSupplied, [text| + , ("defpact_not_init", isExecutionError _NoDefPactIdAndExecEnvSupplied, [text| (module m g (defcap g () true)) (continue-pact 1) |]) diff --git a/pact-tests/constructor-tag-goldens/CoreBuiltin.golden b/pact-tests/constructor-tag-goldens/CoreBuiltin.golden index 273a19728..c2a7dd90d 100644 --- a/pact-tests/constructor-tag-goldens/CoreBuiltin.golden +++ b/pact-tests/constructor-tag-goldens/CoreBuiltin.golden @@ -127,7 +127,8 @@ {"conName":"CoreIdentity","conIndex":"7e"} {"conName":"CoreVerifySPV","conIndex":"7f"} {"conName":"CoreEnforceVerifier","conIndex":"80"} -{"conName":"CoreHyperlaneMessageId","conIndex":"81"} -{"conName":"CoreHyperlaneDecodeMessage","conIndex":"82"} -{"conName":"CoreHyperlaneEncodeMessage","conIndex":"83"} +{"conName":"CoreAcquireModuleAdmin","conIndex":"81"} +{"conName":"CoreHyperlaneMessageId","conIndex":"82"} +{"conName":"CoreHyperlaneDecodeMessage","conIndex":"83"} +{"conName":"CoreHyperlaneEncodeMessage","conIndex":"84"} diff --git a/pact-tests/constructor-tag-goldens/EvalError.golden b/pact-tests/constructor-tag-goldens/EvalError.golden index f69403a79..d9e77b8e4 100644 --- a/pact-tests/constructor-tag-goldens/EvalError.golden +++ b/pact-tests/constructor-tag-goldens/EvalError.golden @@ -73,5 +73,6 @@ {"conName":"InvalidCustomKeysetPredicate","conIndex":"48"} {"conName":"HyperlaneError","conIndex":"49"} {"conName":"HyperlaneDecodeError","conIndex":"4a"} -{"conName":"UnknownException","conIndex":"4b"} +{"conName":"ModuleAdminNotAcquired","conIndex":"4b"} +{"conName":"UnknownException","conIndex":"4c"} diff --git a/pact-tests/gas-goldens/acquire-module-admin.repl b/pact-tests/gas-goldens/acquire-module-admin.repl new file mode 100644 index 000000000..4a28f351c --- /dev/null +++ b/pact-tests/gas-goldens/acquire-module-admin.repl @@ -0,0 +1,10 @@ +(begin-tx) +(module m g + (defcap g () + (map (+ 1) (enumerate 0 20)))) +(commit-tx) + +(begin-tx) +(acquire-module-admin m) +(acquire-module-admin m) +(commit-tx) diff --git a/pact-tests/gas-goldens/builtinGas.golden b/pact-tests/gas-goldens/builtinGas.golden index d16f76b27..87731ec5b 100644 --- a/pact-tests/gas-goldens/builtinGas.golden +++ b/pact-tests/gas-goldens/builtinGas.golden @@ -11,6 +11,7 @@ >=: 114 ^: 5000 abs: 50 +acquire-module-admin: 60045303 add-time: 5000 and?: 253 at: 2500 @@ -89,7 +90,7 @@ read-integer: 128 read-keyset: 37239 read-msg: 128 read-string: 128 -read: 60393378 +read: 60393713 remove: 261 require-capability: 60002677 resume: 60006677 @@ -111,8 +112,8 @@ typeof: 25 update: 60516583 validate-principal: 4940 where: 1079 -with-default-read: 60405928 -with-read: 60394453 +with-default-read: 60406263 +with-read: 60394788 write: 60383378 xor: 2000 yield: 60002449 diff --git a/pact-tests/legacy-serial-tests/coin-v5/coin-v5.repl b/pact-tests/legacy-serial-tests/coin-v5/coin-v5.repl index dc737a859..0ac5d0cbd 100644 --- a/pact-tests/legacy-serial-tests/coin-v5/coin-v5.repl +++ b/pact-tests/legacy-serial-tests/coin-v5/coin-v5.repl @@ -100,7 +100,7 @@ (env-gaslog) (expect "Gas cost of querying the details of an account" - 12 (env-gas)) + 13 (env-gas)) (commit-tx) @@ -394,7 +394,7 @@ (env-gaslog) (expect "Gas cost of transfer" - 338 (env-gas)) + 340 (env-gas)) (expect-failure "emily->doug capability used up" "TRANSFER exceeded" @@ -431,7 +431,7 @@ (transfer-create 'doug 'will (read-keyset 'will) 1.0)) (env-gaslog) (expect - "Gas cost of transfer-create" 332 (env-gas)) + "Gas cost of transfer-create" 333 (env-gas)) (expect "doug now has 0.4 coins" diff --git a/pact-tests/legacy-serial-tests/marmalade-v2/marmalade.repl b/pact-tests/legacy-serial-tests/marmalade-v2/marmalade.repl index 58295e385..74d755a10 100644 --- a/pact-tests/legacy-serial-tests/marmalade-v2/marmalade.repl +++ b/pact-tests/legacy-serial-tests/marmalade-v2/marmalade.repl @@ -98,7 +98,7 @@ (begin-tx) (load "../../pact-tests/marmalade/pact/ledger/ledger-v2.interface.pact") - (load "../../pact-tests/marmalade/pact/policy-manager/sale.interface.pact") + (load "../../pact-tests/marmalade/pact/policy-manager/sale.interface.pact") (load "../../pact-tests/marmalade/pact/policy-manager/policy-manager.pact") (load "../../pact-tests/marmalade/pact/ledger/ledger.pact") (commit-tx) @@ -138,6 +138,7 @@ (use marmalade-v2.ledger) +(acquire-module-admin marmalade-v2.ledger) (create-table versions) (commit-tx) diff --git a/pact-tests/pact-tests/coin-v1.repl b/pact-tests/pact-tests/coin-v1.repl index fd40e1dc2..ec15a590f 100644 --- a/pact-tests/pact-tests/coin-v1.repl +++ b/pact-tests/pact-tests/coin-v1.repl @@ -101,7 +101,7 @@ (env-gaslog) (expect "Gas cost of querying the details of an account" - 12 (env-gas)) + 13 (env-gas)) (commit-tx) @@ -395,7 +395,7 @@ (env-gaslog) (expect "Gas cost of transfer" - 337 (env-gas)) + 339 (env-gas)) (expect-failure "emily->doug capability used up" "TRANSFER exceeded" @@ -432,7 +432,7 @@ (transfer-create 'doug 'will (read-keyset 'will) 1.0)) (env-gaslog) (expect - "Gas cost of transfer-create" 326 (env-gas)) + "Gas cost of transfer-create" 327 (env-gas)) (expect "doug now has 0.4 coins" diff --git a/pact-tests/pact-tests/coin-v5.repl b/pact-tests/pact-tests/coin-v5.repl index 4f3780ccc..14f677708 100644 --- a/pact-tests/pact-tests/coin-v5.repl +++ b/pact-tests/pact-tests/coin-v5.repl @@ -114,7 +114,7 @@ (env-gaslog) (expect "Gas cost of querying the details of an account" - 12 (env-gas)) + 13 (env-gas)) (commit-tx) @@ -408,7 +408,7 @@ (env-gaslog) (expect "Gas cost of transfer" - 338 (env-gas)) + 340 (env-gas)) (expect-failure "emily->doug capability used up" "TRANSFER exceeded" @@ -445,7 +445,7 @@ (transfer-create 'doug 'will (read-keyset 'will) 1.0)) (env-gaslog) (expect - "Gas cost of transfer-create" 332 (env-gas)) + "Gas cost of transfer-create" 333 (env-gas)) (expect "doug now has 0.4 coins" diff --git a/pact-tests/pact-tests/db.repl b/pact-tests/pact-tests/db.repl index 18b3beef8..a56b9f92a 100644 --- a/pact-tests/pact-tests/db.repl +++ b/pact-tests/pact-tests/db.repl @@ -36,11 +36,15 @@ (use dbtest) (begin-tx) (use dbtest) +; Acquire module admin, should pass because admin key is in scope +(expect "Module admin acquired successfully" "Module admin for module dbtest acquired" (acquire-module-admin dbtest)) (insert persons ID_A ROW_A) (expect-failure "dupe key should fail" (insert persons ID_A ROW_A)) (commit-tx) (begin-tx) (use dbtest) +; acquire admin from db test +(acquire-module-admin dbtest) (expect "keys works" [ID_A] (keys persons)) ; (insert stuff "k" { "stuff": { "dec": 1.2, "bool": true, "int": -3, "time": (parse-time "%F" "1970-01-01") } }) diff --git a/pact-tests/pact-tests/gov.repl b/pact-tests/pact-tests/gov.repl index 732eee873..76ba169a4 100644 --- a/pact-tests/pact-tests/gov.repl +++ b/pact-tests/pact-tests/gov.repl @@ -41,14 +41,14 @@ ;; direct read will attempt to grant admin (env-hash (hash "tx1")) -(expect-failure "admin grant fails for tx1" (read votes "bob")) +(expect-failure "admin grant fails for tx1" (acquire-module-admin govtest)) (env-hash (hash "tx3")) -(expect-failure "admin grant fails for tx3" (read votes "bob")) +(expect-failure "admin grant fails for tx3" (acquire-module-admin govtest)) (env-hash (hash "tx2")) -(expect "admin grant succeeds for tx2" { "vote-hash": (hash "tx2") } (read votes "bob")) +(expect "admin grant succeeds for tx2" "Module admin for module govtest acquired" (acquire-module-admin govtest)) ;; prove that admin is granted for rest of tx by resetting tx-hash to non-winning value diff --git a/pact-tests/pact-tests/leftpad.repl b/pact-tests/pact-tests/leftpad.repl index f85735fe5..262014dc0 100644 --- a/pact-tests/pact-tests/leftpad.repl +++ b/pact-tests/pact-tests/leftpad.repl @@ -31,6 +31,7 @@ (expect "leftpad works" " hello" (dep.dep-leftpad)) (dep.dep-impure "a" 1) +(acquire-module-admin impure) (expect "impure works" { "value": 1 } (read impure.foo "a")) (rollback-tx) @@ -71,4 +72,5 @@ (commit-tx) (dep.dep-impure "b" 1) +(acquire-module-admin impure) (expect "impure works with blessed hash" { "value": 1 } (read impure.foo "b")) diff --git a/pact-tests/pact-tests/side-effects.repl b/pact-tests/pact-tests/side-effects.repl index 678557aaf..c7e37b4a1 100644 --- a/pact-tests/pact-tests/side-effects.repl +++ b/pact-tests/pact-tests/side-effects.repl @@ -1,4 +1,4 @@ - +(begin-tx) (module m g (defcap g () true) @@ -36,3 +36,28 @@ (expect "counter is zero" 0 (get-counter)) (expect "counter increments twice" 2 (do (increment-counter) (increment-counter) (get-counter))) (expect "counter decrements twice" 0 (do (decrement-counter) (decrement-counter) (get-counter))) + +(commit-tx) + +; redeploy m +(begin-tx) + +(module m g + (defcap g () + (map (+ 1) (enumerate 0 100)) + )) +(commit-tx) + +(begin-tx) +(env-gasmodel "table") +(env-gaslimit 100000) +(env-gas 0) +(acquire-module-admin m) +(expect "Acquiring module admin gas cost evaluates enumerate" 44 (env-gas)) +(acquire-module-admin m) +; Note: The gas cost here is: the flat cost of acquire-module-admin +; as a native + the gas from the previous test. In other words: +; this is testing that the only gas consumed is from the flat native cost, which means +; we didn't evaluate the cap twice +(expect "Acquiring module admin twice does not evaluate the cap twice" (+ 20 44) (env-gas)) +(commit-tx) diff --git a/pact-tests/pact-tests/try.repl b/pact-tests/pact-tests/try.repl index 6e6ff3954..3bb2b9ff8 100644 --- a/pact-tests/pact-tests/try.repl +++ b/pact-tests/pact-tests/try.repl @@ -60,6 +60,7 @@ (expect-failure "failure on attempting to write to the db" (failed-write)) ;; failure case for reads in pure context +(acquire-module-admin try-module) (insert test-table "emily" { "test" : true }) (expect "failure when on attempting to reads from the db" true (successful-read)) diff --git a/pact-tng.cabal b/pact-tng.cabal index 337ba5ed3..264b0de01 100644 --- a/pact-tng.cabal +++ b/pact-tng.cabal @@ -335,6 +335,8 @@ executable gasmodel , criterion , terminal-progress-bar , neat-interpolation + , hedgehog + , pact-tng:test-utils other-modules: Pact.Core.GasModel.BuiltinsGas @@ -463,6 +465,7 @@ library test-utils , pact-tng:unsafe , lsp-test >= 0.17 , lsp-types + , pact-time exposed-modules: , Pact.Core.Gen diff --git a/pact/Pact/Core/Builtin.hs b/pact/Pact/Core/Builtin.hs index 5fced5a6d..8d9e5baa1 100644 --- a/pact/Pact/Core/Builtin.hs +++ b/pact/Pact/Core/Builtin.hs @@ -215,6 +215,7 @@ data CoreBuiltin | CoreIdentity | CoreVerifySPV | CoreEnforceVerifier + | CoreAcquireModuleAdmin -- Hyperlane | CoreHyperlaneMessageId | CoreHyperlaneDecodeMessage @@ -350,6 +351,7 @@ coreBuiltinToText = \case CoreWithRead -> "with-read" CoreWrite -> "write" CoreTxHash -> "tx-hash" + CoreAcquireModuleAdmin -> "acquire-module-admin" CoreAndQ -> "and?" CoreOrQ -> "or?" CoreWhere -> "where" @@ -536,6 +538,7 @@ coreBuiltinToUserText = \case CoreHyperlaneMessageId -> "hyperlane-message-id" CoreHyperlaneDecodeMessage -> "hyperlane-decode-token-message" CoreHyperlaneEncodeMessage -> "hyperlane-encode-token-message" + CoreAcquireModuleAdmin -> "acquire-module-admin" instance IsBuiltin CoreBuiltin where builtinName = NativeName . coreBuiltinToText @@ -682,6 +685,7 @@ instance IsBuiltin CoreBuiltin where CoreDec -> 1 CoreCond -> 1 CoreIdentity -> 1 + CoreAcquireModuleAdmin -> 1 CoreVerifySPV -> 2 CoreEnforceVerifier -> 1 CoreHyperlaneMessageId -> 1 diff --git a/pact/Pact/Core/Compile.hs b/pact/Pact/Core/Compile.hs index 6a0d2ead8..50828d358 100644 --- a/pact/Pact/Core/Compile.hs +++ b/pact/Pact/Core/Compile.hs @@ -117,13 +117,12 @@ evalModuleGovernance -> EvalM e b i () evalModuleGovernance interpreter tl = do lo <- use esLoaded - pdb <- viewEvalEnv eePactDb case tl of Lisp.TLModule m -> do let info = Lisp._mInfo m let unmangled = Lisp._mName m mname <- mangleNamespace unmangled - lookupModule (Lisp._mInfo m) pdb mname >>= \case + lookupModule (Lisp._mInfo m) mname >>= \case Just targetModule -> do case _mGovernance targetModule of KeyGov ksn -> do @@ -133,14 +132,13 @@ evalModuleGovernance interpreter tl = do void $ eval interpreter PImpure term CapGov (FQName fqn) -> do hasModAdmin <- uses (esCaps . csModuleAdmin) (S.member mname) + -- check whether we already have module admin. + -- if we do, we don't need to run governance if hasModAdmin then pure () else do - -- check whether we already have module admin. - -- if we do, we don't need to run this. - let cgBody = Constant LUnit info - withCapApp = App (Var (fqnToName fqn) info) [] info - term = CapabilityForm (WithCapability withCapApp cgBody) info - void $ eval interpreter PImpure term + let unitBody = Constant LUnit info + let ct = CapToken (fqnToQualName fqn) [] + void $ evalWithCapability interpreter info PImpure ct unitBody esCaps . csModuleAdmin %= S.insert mname -- | Restore the state to pre-module admin acquisition esLoaded .= lo @@ -149,7 +147,7 @@ evalModuleGovernance interpreter tl = do let info = Lisp._ifInfo iface let unmangled = Lisp._ifName iface ifn <- mangleNamespace unmangled - lookupModuleData info pdb ifn >>= \case + lookupModuleData info ifn >>= \case Nothing -> enforceNamespaceInstall info interpreter Just _ -> throwExecutionError info (CannotUpgradeInterface ifn) diff --git a/pact/Pact/Core/Environment/Utils.hs b/pact/Pact/Core/Environment/Utils.hs index 20025bd30..61976e619 100644 --- a/pact/Pact/Core/Environment/Utils.hs +++ b/pact/Pact/Core/Environment/Utils.hs @@ -10,12 +10,6 @@ module Pact.Core.Environment.Utils ( viewEvalEnv , viewsEvalEnv - , getModuleData - , getModule - , getModuleMember - , getModuleMemberWithHash - , lookupModule - , lookupModuleData , throwExecutionError , toFqDep , mangleNamespace @@ -107,76 +101,6 @@ throwNativeExecutionError info b msg = throwExecutionError info (NativeExecutionError (builtinName b) msg) --- | lookupModuleData for only modules -lookupModule :: i -> PactDb b i -> ModuleName -> EvalM e b i (Maybe (EvalModule b i)) -lookupModule info pdb mn = - use (esLoaded . loModules . at mn) >>= \case - Just (ModuleData md _) -> pure (Just md) - Just (InterfaceData _ _) -> - throwExecutionError info (ExpectedModule mn) - Nothing -> do - liftDbFunction info (_pdbRead pdb DModules mn) >>= \case - Just mdata@(ModuleData md deps) -> do - let newLoaded = M.fromList $ toFqDep mn (_mHash md) <$> _mDefs md - (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps - (esLoaded . loModules) %= M.insert mn mdata - pure (Just md) - Just (InterfaceData _ _) -> - throwExecutionError info (ExpectedModule mn) - Nothing -> pure Nothing - --- | lookupModuleData modules and interfaces -lookupModuleData :: i -> PactDb b i -> ModuleName -> EvalM e b i (Maybe (ModuleData b i)) -lookupModuleData info pdb mn = - use (esLoaded . loModules . at mn) >>= \case - Just md -> pure (Just md) - Nothing -> do - liftDbFunction info (_pdbRead pdb DModules mn) >>= \case - Just mdata@(ModuleData md deps) -> do - let newLoaded = M.fromList $ toFqDep mn (_mHash md) <$> _mDefs md - (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps - (esLoaded . loModules) %= M.insert mn mdata - pure (Just mdata) - Just mdata@(InterfaceData iface deps) -> do - let ifDefs = mapMaybe ifDefToDef (_ifDefns iface) - let newLoaded = M.fromList $ toFqDep mn (_ifHash iface) <$> ifDefs - (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps - (esLoaded . loModules) %= M.insert mn mdata - pure (Just mdata) - Nothing -> pure Nothing - - --- | getModuleData, but only for modules, no interfaces -getModule :: i -> PactDb b i -> ModuleName -> EvalM e b i (EvalModule b i) -getModule info pdb mn = lookupModule info pdb mn >>= \case - Just md -> pure md - Nothing -> throwExecutionError info (ModuleDoesNotExist mn) - --- | Get or load a module or interface based on the module name -getModuleData :: i -> PactDb b i -> ModuleName -> EvalM e b i (ModuleData b i) -getModuleData info pdb mn = lookupModuleData info pdb mn >>= \case - Just md -> pure md - Nothing -> throwExecutionError info (ModuleDoesNotExist mn) - --- | Returns a module member, but only for modules, no interfaces -getModuleMember :: i -> PactDb b i -> QualifiedName -> EvalM e b i (EvalDef b i) -getModuleMember info pdb (QualifiedName qn mn) = do - md <- getModule info pdb mn - case findDefInModule qn md of - Just d -> pure d - Nothing -> do - let fqn = FullyQualifiedName mn qn (_mHash md) - throwExecutionError info (ModuleMemberDoesNotExist fqn) - -getModuleMemberWithHash :: i -> PactDb b i -> QualifiedName -> EvalM e b i (EvalDef b i, ModuleHash) -getModuleMemberWithHash info pdb (QualifiedName qn mn) = do - md <- getModule info pdb mn - case findDefInModule qn md of - Just d -> pure (d, _mHash md) - Nothing -> do - let fqn = FullyQualifiedName mn qn (_mHash md) - throwExecutionError info (ModuleMemberDoesNotExist fqn) - mangleNamespace :: ModuleName -> EvalM e b i ModuleName mangleNamespace mn@(ModuleName mnraw ns) = diff --git a/pact/Pact/Core/Errors.hs b/pact/Pact/Core/Errors.hs index 23c190fa8..5000f3de6 100644 --- a/pact/Pact/Core/Errors.hs +++ b/pact/Pact/Core/Errors.hs @@ -515,6 +515,8 @@ data EvalError -- ^ Hyperlane error | HyperlaneDecodeError HyperlaneDecodeError -- ^ Hyperlane decoding error + | ModuleAdminNotAcquired ModuleName + -- ^ Module admin was needed for a particular operation, but has not been acquired. | UnknownException -- ^ Used by chainweb for unknown exceptions deriving (Eq, Show, Generic) @@ -710,6 +712,8 @@ instance Pretty EvalError where "Invalid custom predicate for keyset" <+> pretty pn HyperlaneError he -> "Hyperlane native error:" <+> pretty he HyperlaneDecodeError he -> "Hyperlane decode error:" <+> pretty he + ModuleAdminNotAcquired mn -> + "Module admin necessary for operation but has not been acquired:" <> pretty mn UnknownException -> "Unknown exception" diff --git a/pact/Pact/Core/Evaluate.hs b/pact/Pact/Core/Evaluate.hs index 0962efb05..cc72d49cc 100644 --- a/pact/Pact/Core/Evaluate.hs +++ b/pact/Pact/Core/Evaluate.hs @@ -60,7 +60,6 @@ import Pact.Core.IR.Desugar import Pact.Core.Verifiers import Pact.Core.Interpreter import Pact.Core.Info -import Pact.Core.IR.Eval.Runtime.Utils import qualified Pact.Core.IR.Eval.CEK as Eval import qualified Pact.Core.IR.Eval.Direct.Evaluator as Direct import qualified Pact.Core.Syntax.Lexer as Lisp @@ -77,22 +76,26 @@ type PactTxResult a = evalInterpreter :: Interpreter ExecRuntime CoreBuiltin i evalInterpreter = - Interpreter runGuard runTerm resume + Interpreter runGuard runTerm resume evalWithCap where runTerm purity term = Eval.eval purity cekEnv term runGuard info g = Eval.interpretGuard info cekEnv g resume info defPact = Eval.evalResumePact info cekEnv defPact + evalWithCap info purity ct term = + Eval.evalWithinCap info purity cekEnv ct term cekEnv :: Eval.BuiltinEnv ExecRuntime CoreBuiltin i cekEnv = coreBuiltinEnv @ExecRuntime evalDirectInterpreter :: Interpreter ExecRuntime CoreBuiltin i evalDirectInterpreter = - Interpreter runGuard runTerm resume + Interpreter runGuard runTerm resume evalWithCap where runTerm purity term = Direct.eval purity env term runGuard info g = Direct.interpretGuard info env g resume info defPact = Direct.evalResumePact info env defPact + evalWithCap info purity ct term = + Direct.evalWithinCap info purity env ct term env = Direct.coreBuiltinEnv -- | Transaction-payload related environment data. @@ -301,15 +304,13 @@ evalWithinCap -> EvalEnv CoreBuiltin Info -> EvalState CoreBuiltin Info -> IO (PactTxResult ()) -evalWithinCap (CapToken qualName pvs) body ee es = +evalWithinCap ct body ee es = evalWithinTx' ee es runInput where runInput = do let info = view Lisp.termInfo body (DesugarOutput term' _) <- runDesugarTerm body - (_, mh) <- getDefCapQN info qualName - let fqCt = CapToken (qualNameToFqn qualName mh) pvs - () <$ Eval.evalWithinCap PImpure cekEnv fqCt term' + () <$ Eval.evalWithinCap info PImpure cekEnv ct term' -- | Evaluate some input action within a tx context diff --git a/pact/Pact/Core/Gas/TableGasModel.hs b/pact/Pact/Core/Gas/TableGasModel.hs index ecef0e0b3..5f0126d3a 100644 --- a/pact/Pact/Core/Gas/TableGasModel.hs +++ b/pact/Pact/Core/Gas/TableGasModel.hs @@ -560,6 +560,7 @@ coreBuiltinGasCost = MilliGas . \case CoreHyperlaneMessageId -> 2_000 CoreHyperlaneDecodeMessage -> 2_000 CoreHyperlaneEncodeMessage -> 2_000 + CoreAcquireModuleAdmin -> 20_000 {-# INLINABLE runTableModel #-} diff --git a/pact/Pact/Core/IR/Desugar.hs b/pact/Pact/Core/IR/Desugar.hs index 2c0046c39..967e08ad6 100644 --- a/pact/Pact/Core/IR/Desugar.hs +++ b/pact/Pact/Core/IR/Desugar.hs @@ -915,8 +915,7 @@ resolveModuleName i mn@(ModuleName name mNs) = Just (CurrModule currMod imps MTModule) | currMod == mn -> pure (currMod, imps) | otherwise -> do - pdb <- viewEvalEnv' eePactDb - lift (lookupModuleData i pdb mn) >>= \case + lift (lookupModuleData i mn) >>= \case Just md -> getModName md Nothing -> case mNs of Just _ -> throwDesugarError (NoSuchModule mn) i @@ -927,7 +926,7 @@ resolveModuleName i mn@(ModuleName name mNs) = Just (Namespace ns _ _) | ModuleName name (Just ns) == currMod -> pure (currMod, imps) | otherwise -> - lift (getModuleData i pdb (ModuleName name (Just ns))) >>= getModName + lift (getModuleData i (ModuleName name (Just ns))) >>= getModName _ -> resolveModuleData mn i >>= getModName where getModName = \case @@ -950,8 +949,7 @@ resolveInterfaceName i mn@(ModuleName name mNs) = Just (CurrModule currMod _ MTInterface) | currMod == mn -> pure mn | otherwise -> do - pdb <- viewEvalEnv' eePactDb - lift (lookupModuleData i pdb mn) >>= \case + lift (lookupModuleData i mn) >>= \case Just (InterfaceData _ _) -> pure mn Just _ -> throwDesugarError (InvalidModuleReference mn) i Nothing -> case mNs of @@ -963,7 +961,7 @@ resolveInterfaceName i mn@(ModuleName name mNs) = Just (Namespace ns _ _) | ModuleName name (Just ns) == currMod -> pure currMod | otherwise -> - lift (getModuleData i pdb (ModuleName name (Just ns))) >>= getModName + lift (getModuleData i (ModuleName name (Just ns))) >>= getModName _ -> resolveModuleData mn i >>= getModName where getModName = \case @@ -978,15 +976,14 @@ resolveModuleData -> i -> RenamerM e b i (ModuleData b i) resolveModuleData mn@(ModuleName name mNs) i = do - pdb <- viewEvalEnv' eePactDb - lift (lookupModuleData i pdb mn) >>= \case + lift (lookupModuleData i mn) >>= \case Just md -> pure md Nothing -> case mNs of Just _ -> throwDesugarError (NoSuchModule mn) i Nothing -> useEvalState (esLoaded . loNamespace) >>= \case Nothing -> throwDesugarError (NoSuchModule mn) i Just (Namespace ns _ _) -> - lift (getModuleData i pdb (ModuleName name (Just ns))) + lift (getModuleData i (ModuleName name (Just ns))) renameType :: (DesugarBuiltin b) @@ -1347,8 +1344,7 @@ resolveQualified -> i -> RenamerM e b i (Name, Maybe DefKind) resolveQualified (QualifiedName qn qmn@(ModuleName modName mns)) i = do - pdb <- viewEvalEnv' eePactDb - runMaybeT (baseLookup pdb qn qmn <|> modRefLookup pdb <|> namespacedLookup pdb) >>= \case + runMaybeT (baseLookup qn qmn <|> modRefLookup <|> namespacedLookup) >>= \case Just p -> pure p Nothing -> throwDesugarError (NoSuchModuleMember qmn qn) i where @@ -1357,8 +1353,8 @@ resolveQualified (QualifiedName qn qmn@(ModuleName modName mns)) i = do guard (currMod == moduleName) (nk, dk) <- MaybeT $ view (reCurrModuleTmpBinds . at defnName) pure (Name defnName nk, Just dk) - baseLookup pdb defnName moduleName = lookupLocalQual defnName moduleName <|> do - MaybeT (lift (lookupModuleData i pdb moduleName)) >>= \case + baseLookup defnName moduleName = lookupLocalQual defnName moduleName <|> do + MaybeT (lift (lookupModuleData i moduleName)) >>= \case ModuleData module' _ -> do d <- hoistMaybe (findDefInModule defnName module' ) lift $ rsDependencies %= S.insert moduleName @@ -1368,18 +1364,18 @@ resolveQualified (QualifiedName qn qmn@(ModuleName modName mns)) i = do d <- hoistMaybe (findDefInInterface defnName iface) lift $ rsDependencies %= S.insert moduleName pure (Name qn (NTopLevel moduleName (_ifHash iface)), Just (defKind ifn d)) - modRefLookup pdb = case mns of + modRefLookup = case mns of -- Fail eagerly: the previous lookup was fully qualified Just _ -> MaybeT (throwDesugarError (NoSuchModuleMember qmn qn) i) Nothing -> do let mn' = ModuleName qn (Just (NamespaceName modName)) - m <- MaybeT $ lift $ lookupModule i pdb mn' + m <- MaybeT $ lift $ lookupModule i mn' let nk = NModRef mn' (_mImplements m) pure (Name qn nk, Nothing) - namespacedLookup pdb = do + namespacedLookup = do Namespace ns _ _ <- MaybeT (useEvalState (esLoaded . loNamespace)) let mn' = ModuleName modName (Just ns) - baseLookup pdb qn mn' + baseLookup qn mn' -- | Handle all name resolution for modules renameModule @@ -1425,6 +1421,8 @@ renameModule (Module unmangled mgov defs blessed imports implements mhash txh i) case find (\d -> BN (BareName (defName d)) == govName) defs of Just (DCap d) -> do let fqn = FullyQualifiedName mname (_argName $ _dcapSpec d) mhash + let dcName = (_argName . _dcapSpec) d + unless (null (_dcapArgs d)) $ throwDesugarError (InvalidGovernanceRef (QualifiedName dcName mname)) i pure (CapGov (FQName fqn)) Just d -> throwDesugarError (InvalidGovernanceRef (QualifiedName (defName d) mname)) i Nothing -> throwDesugarError (InvalidGovernanceRef (QualifiedName (rawParsedName govName) mname)) i diff --git a/pact/Pact/Core/IR/Eval/CEK.hs b/pact/Pact/Core/IR/Eval/CEK.hs index b1f41a78a..d5bde535b 100644 --- a/pact/Pact/Core/IR/Eval/CEK.hs +++ b/pact/Pact/Core/IR/Eval/CEK.hs @@ -18,7 +18,6 @@ module Pact.Core.IR.Eval.CEK , resumePact , evalCap , nameToFQN - , guardTable , isKeysetInSigs , isKeysetNameInSigs , requireCap @@ -157,13 +156,11 @@ evaluateTerm cont handler env (Var n info) = do failInvariant info (InvariantInvalidDefKind (defKind mname d) "in var position") Nothing -> failInvariant info (InvariantInvalidBoundVariable (_nName n)) - NModRef m ifs -> case ifs of - [] -> throwExecutionError info (ModRefImplementsNoInterfaces m) - _ -> + NModRef m ifs -> returnCEKValue cont handler (VModRef (ModRef m (S.fromList ifs))) NDynRef (DynamicRef dArg i) -> case RAList.lookup (view ceLocal env) i of Just (VModRef mr) -> do - modRefHash <- _mHash <$> getModule info (view cePactDb env) (_mrModule mr) + modRefHash <- _mHash <$> getModule info (_mrModule mr) let nk = NTopLevel (_mrModule mr) modRefHash evalCEK cont handler env (Var (Name dArg nk) info) Just _ -> @@ -363,7 +360,7 @@ applyPact -> EvalM e b i (EvalResult e b i) applyPact i pc ps cont handler cenv nested = use esDefPactExec >>= \case Just pe -> throwExecutionError i (MultipleOrNestedDefPactExecFound pe) - Nothing -> getModuleMemberWithHash i (_cePactDb cenv) (pc ^. pcName) >>= \case + Nothing -> getModuleMemberWithHash i (pc ^. pcName) >>= \case (DPact defPact, mh) -> do let nSteps = NE.length (_dpSteps defPact) @@ -430,7 +427,7 @@ applyNestedPact applyNestedPact i pc ps cont handler cenv = use esDefPactExec >>= \case Nothing -> failInvariant i $ InvariantPactExecNotInEnv (Just pc) - Just pe -> getModuleMemberWithHash i (_cePactDb cenv) (pc ^. pcName) >>= \case + Just pe -> getModuleMemberWithHash i (pc ^. pcName) >>= \case (DPact defPact, mh) -> do step <- maybe (throwExecutionError i (InvalidDefPactStepSupplied ps (_peStepCount pe))) pure $ _dpSteps defPact ^? ix (ps ^. psStep) @@ -494,7 +491,7 @@ resumePact i cont handler env crossChainContinuation = viewEvalEnv eeDefPactStep Nothing -> throwExecutionError i DefPactStepNotInEnvironment -- TODO check with multichain Just ps -> do pdb <- viewEvalEnv eePactDb - dbState <- liftDbFunction i (_pdbRead pdb DDefPacts (_psDefPactId ps)) + dbState <- liftGasM i (_pdbRead pdb DDefPacts (_psDefPactId ps)) case (dbState, crossChainContinuation) of -- Terminate defpact in db: always fail @@ -565,53 +562,27 @@ nameToFQN info env (Name n nk) = case nk of NTopLevel mn mh -> pure (FullyQualifiedName mn n mh) NDynRef (DynamicRef dArg i) -> case RAList.lookup (view ceLocal env) i of Just (VModRef mr) -> do - md <- getModule info (view cePactDb env) (_mrModule mr) + md <- getModule info (_mrModule mr) pure (FullyQualifiedName (_mrModule mr) dArg (_mHash md)) Just _ -> throwExecutionError info (DynNameIsNotModRef n) Nothing -> failInvariant info (InvariantInvalidBoundVariable n) _ -> failInvariant info (InvariantInvalidBoundVariable n) -guardTable - :: () - => i - -> Cont e b i - -> CEKErrorHandler e b i - -> CEKEnv e b i - -> TableValue - -> GuardTableOp - -> EvalM e b i (EvalResult e b i) -guardTable i cont handler env (TableValue tn mh _) dbop = do - let mn = _tableModuleName tn - checkLocalBypass $ - guardForModuleCall i cont handler env mn $ do - mdl <- getModule i (view cePactDb env) mn - enforceBlessedHashes i mdl mh - returnCEKValue cont handler VUnit - where - checkLocalBypass notBypassed = do - enabled <- isExecutionFlagSet FlagAllowReadInLocal - case dbop of - GtWrite -> notBypassed - GtCreateTable -> notBypassed - _ | enabled -> returnCEKValue cont handler VUnit - | otherwise -> notBypassed - -guardForModuleCall - :: () - => i - -> Cont e b i - -> CEKErrorHandler e b i - -> CEKEnv e b i - -> ModuleName - -> EvalM e b i (EvalResult e b i) - -> EvalM e b i (EvalResult e b i) -guardForModuleCall i cont handler env currMod onFound = - findCallingModule >>= \case - Just mn | mn == currMod -> onFound - _ -> do - mc <- use (esCaps . csModuleAdmin) - if S.member currMod mc then onFound - else getModule i (view cePactDb env) currMod >>= acquireModuleAdmin i cont handler env + +-- guardForModuleCall +-- :: () +-- => i +-- -> ModuleName +-- -> EvalM e b i a +-- -> EvalM e b i a +-- guardForModuleCall i currMod onFound = +-- findCallingModule >>= \case +-- Just mn | mn == currMod -> onFound +-- _ -> do +-- mc <- use (esCaps . csModuleAdmin) +-- if S.member currMod mc then onFound +-- else +-- throwExecutionError i (ModuleAdminNotAcquired currMod) -- | Acquires module admin for a known module -- NOTE: This function should only be called _after_ @@ -626,7 +597,10 @@ acquireModuleAdmin -> EvalModule b i -> EvalM e b i (EvalResult e b i) acquireModuleAdmin i cont handler env mdl = do - case _mGovernance mdl of + let mname = _mName mdl + moduleAdminAcquired <- S.member mname <$> use (esCaps . csModuleAdmin) + if moduleAdminAcquired then returnCEKValue cont handler VUnit + else case _mGovernance mdl of KeyGov ksn -> do let cont' = ModuleAdminC (_mName mdl) cont isKeysetNameInSigs i cont' handler env ksn @@ -669,7 +643,6 @@ pushStackFrame info cont mty sf = do esCheckRecursion %= NE.cons (RecursionCheck (S.insert qn currentCalled)) - -- | Our main workhorse for "Evaluate a capability, then do something else" -- `evalCap` handles -- - with-capability @@ -1110,12 +1083,10 @@ applyContToValue (CondC env info frame cont) handler v = do _ -> -- Note: a non-boolean value in these functions is non recoverable throwExecutionError info ExpectedPactValue -applyContToValue currCont@(CapInvokeC env info cf cont) handler v = case cf of +applyContToValue (CapInvokeC env info cf cont) handler v = case cf of WithCapC body -> case v of VCapToken ct@(CapToken fqn _) -> do - -- Todo: CEK-style this - let cont' = IgnoreValueC (PCapToken ct) currCont - guardForModuleCall info cont' handler env (_fqModule fqn) $ + guardForModuleCall info (_fqModule fqn) $ evalCap info cont handler env ct PopCapInvoke NormalCapEval body -- Todo: this is actually more like "expected cap token" VPactValue v' -> throwExecutionError info $ ExpectedCapToken v' @@ -1170,45 +1141,45 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do -- Db frames -- Todo: gas costs if post-read actions --------------------------------------------------------- - PreSelectC tv clo mf -> do - keys <- liftDbFunction info (_pdbKeys pdb (tvToDomain tv)) - selectRead tv clo keys [] mf + -- PreSelectC tv clo mf -> do + -- keys <- liftGasM info (_pdbKeys pdb (tvToDomain tv)) + -- selectRead tv clo keys [] mf SelectC tv clo rdata remaining acc mf -> case v of PBool b -> do let acc' = if b then rdata:acc else acc selectRead tv clo remaining acc' mf _ -> throwExecutionError info $ ExpectedBoolValue v - ReadC tv rowkey -> do - liftDbFunction info (_pdbRead pdb (tvToDomain tv) rowkey) >>= \case - Just (RowData rdata) -> - returnCEKValue cont handler (VObject rdata) - Nothing -> - returnCEKError info cont handler $ - NoSuchObjectInDb (_tvName tv) rowkey - WithDefaultReadC tv rowkey (ObjectData defaultObj) clo -> do - liftDbFunction info (_pdbRead pdb (tvToDomain tv) rowkey) >>= \case - Just (RowData rdata) -> - applyLam clo [VObject rdata] cont handler - Nothing -> applyLam clo [VObject defaultObj] cont handler - KeysC tv -> do - ks <- liftDbFunction info (_pdbKeys pdb (tvToDomain tv)) - let li = V.fromList (PString . _rowKey <$> ks) - returnCEKValue cont handler (VList li) - WriteC tv wt rk (ObjectData rv) -> do - let check' = if wt == Update then checkPartialSchema else checkSchema - if check' rv (_tvSchema tv) then do - let rdata = RowData rv - rvSize <- sizeOf info SizeOfV0 rv - chargeGasArgs info (GWrite rvSize) - evalWrite info pdb wt (tvToDomain tv) rk rdata - returnCEKValue cont handler (VString "Write succeeded") - else - throwExecutionError info (WriteValueDidNotMatchSchema (_tvSchema tv) (ObjectData rv)) - PreFoldDbC tv queryClo appClo -> do - let tblDomain = DUserTables (_tvName tv) - -- Todo: keys gas - keys <- liftDbFunction info (_pdbKeys pdb tblDomain) - foldDBRead tv queryClo appClo keys [] + -- ReadC tv rowkey -> do + -- liftGasM info (_pdbRead pdb (tvToDomain tv) rowkey) >>= \case + -- Just (RowData rdata) -> + -- returnCEKValue cont handler (VObject rdata) + -- Nothing -> + -- returnCEKError info cont handler $ + -- NoSuchObjectInDb (_tvName tv) rowkey + -- WithDefaultReadC tv rowkey (ObjectData defaultObj) clo -> do + -- liftGasM info (_pdbRead pdb (tvToDomain tv) rowkey) >>= \case + -- Just (RowData rdata) -> + -- applyLam clo [VObject rdata] cont handler + -- Nothing -> applyLam clo [VObject defaultObj] cont handler + -- KeysC tv -> do + -- ks <- liftGasM info (_pdbKeys pdb (tvToDomain tv)) + -- let li = V.fromList (PString . _rowKey <$> ks) + -- returnCEKValue cont handler (VList li) + -- WriteC tv wt rk (ObjectData rv) -> do + -- let check' = if wt == Update then checkPartialSchema else checkSchema + -- if check' rv (_tvSchema tv) then do + -- let rdata = RowData rv + -- rvSize <- sizeOf info SizeOfV0 rv + -- chargeGasArgs info (GWrite rvSize) + -- evalWrite info pdb wt (tvToDomain tv) rk rdata + -- returnCEKValue cont handler (VString "Write succeeded") + -- else + -- throwExecutionError info (WriteValueDidNotMatchSchema (_tvSchema tv) (ObjectData rv)) + -- PreFoldDbC tv queryClo appClo -> do + -- let tblDomain = DUserTables (_tvName tv) + -- -- Todo: keys gas + -- keys <- liftGasM info (_pdbKeys pdb tblDomain) + -- foldDBRead tv queryClo appClo keys [] FoldDbFilterC tv queryClo appClo (rk, ObjectData om) remaining accum -> case v of PBool b -> do let accum' = if b then (rk, PObject om):accum else accum @@ -1221,17 +1192,17 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do cont' = BuiltinC env info rdf cont applyLam appClo [VString rk, VPactValue pv] cont' handler [] -> returnCEKValue cont handler (VList (V.fromList (v:acc))) - CreateTableC (TableValue tn _ _) -> do - evalCreateUserTable info pdb tn - returnCEKValue cont handler (VString "TableCreated") - EmitEventC ct@(CapToken fqn _) -> do - d <- getDefCap info fqn - enforceMeta (_dcapMeta d) - emitCapability info ct - returnCEKValue cont handler (VBool True) - where - enforceMeta Unmanaged = throwExecutionError info (InvalidEventCap fqn) - enforceMeta _ = pure () + -- CreateTableC (TableValue tn _ _) -> do + -- evalCreateUserTable info pdb tn + -- returnCEKValue cont handler (VString "TableCreated") + -- EmitEventC ct@(CapToken fqn _) -> do + -- d <- getDefCap info fqn + -- enforceMeta (_dcapMeta d) + -- emitCapability info ct + -- returnCEKValue cont handler (VBool True) + -- where + -- enforceMeta Unmanaged = throwExecutionError info (InvalidEventCap fqn) + -- enforceMeta _ = pure () DefineKeysetC ksn newKs -> do newKsSize <- sizeOf info SizeOfV0 newKs chargeGasArgs info (GWrite newKsSize) @@ -1260,7 +1231,7 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do where foldDBRead tv queryClo appClo remaining acc = case remaining of - rk@(RowKey raw):remaining' -> liftDbFunction info (_pdbRead pdb (tvToDomain tv) rk) >>= \case + rk@(RowKey raw):remaining' -> liftGasM info (_pdbRead pdb (tvToDomain tv) rk) >>= \case Just (RowData row) -> do let rdf = FoldDbFilterC tv queryClo appClo (rk, ObjectData row) remaining' acc cont' = BuiltinC env info rdf cont @@ -1274,7 +1245,7 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do applyLam appClo [VString rk, VPactValue pv] cont' handler [] -> returnCEKValue cont handler (VList mempty) selectRead tv clo keys acc mf = case keys of - k:ks -> liftDbFunction info (_pdbRead pdb (tvToDomain tv) k) >>= \case + k:ks -> liftGasM info (_pdbRead pdb (tvToDomain tv) k) >>= \case Just (RowData r) -> do let bf = SelectC tv clo (ObjectData r) ks acc mf cont' = BuiltinC env info bf cont @@ -1629,7 +1600,7 @@ enforceGuard info cont handler env g = case g of GModuleGuard (ModuleGuard mn _) -> calledByModule mn >>= \case True -> returnCEKValue cont handler (VBool True) False -> do - md <- getModule info (view cePactDb env) mn + md <- getModule info mn let cont' = IgnoreValueC (PBool True) cont acquireModuleAdmin info cont' handler env md -- returnCEKValue cont handler (VBool True)guard @@ -1671,7 +1642,7 @@ runUserGuard -> UserGuard QualifiedName PactValue -> EvalM e b i (EvalResult e b i) runUserGuard info cont handler env (UserGuard qn args) = - getModuleMemberWithHash info (_cePactDb env) qn >>= \case + getModuleMemberWithHash info qn >>= \case (Dfun d, mh) -> do when (length (_dfunArgs d) /= length args) $ throwExecutionError info CannotApplyPartialClosure let env' = sysOnlyEnv env @@ -1703,13 +1674,16 @@ eval purity benv term = do evalWithinCap :: forall e b i . () - => Purity + => i + -> Purity -> BuiltinEnv e b i - -> CapToken FullyQualifiedName PactValue + -> CapToken QualifiedName PactValue -> EvalTerm b i -> EvalM e b i PactValue -evalWithinCap purity benv ct term = do +evalWithinCap info purity benv (CapToken qualName vs) term = do ee <- viewEvalEnv id + (_, mh) <- getDefCapQN info qualName + let ct = CapToken (qualNameToFqn qualName mh) vs let cekEnv = envFromPurity purity (CEKEnv mempty (_eePactDb ee) benv (_eeDefPactStep ee) False) evalCap (view termInfo term) Mt CEKNoHandler cekEnv ct PopCapInvoke NormalCapEval term >>= \case @@ -1790,8 +1764,7 @@ isKeysetInSigs info cont handler env ks@(KeySet kskeys ksPred) = do CustomPredicate n -> runCustomPred matched n runCustomPred matched = \case TQN qn -> do - pdb <- viewEvalEnv eePactDb - getModuleMemberWithHash info pdb qn >>= \case + getModuleMemberWithHash info qn >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn qn mh) env let cont' = BuiltinC env info (RunKeysetPredC ks) cont @@ -1819,7 +1792,7 @@ isKeysetNameInSigs -> EvalM e b i (EvalResult e b i) isKeysetNameInSigs info cont handler env ksn = do pdb <- viewEvalEnv eePactDb - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just ks -> isKeysetInSigs info cont handler env ks Nothing -> throwExecutionError info (NoSuchKeySet ksn) diff --git a/pact/Pact/Core/IR/Eval/CEK/Types.hs b/pact/Pact/Core/IR/Eval/CEK/Types.hs index 3d20b7ccd..0baa38b4a 100644 --- a/pact/Pact/Core/IR/Eval/CEK/Types.hs +++ b/pact/Pact/Core/IR/Eval/CEK/Types.hs @@ -86,7 +86,6 @@ module Pact.Core.IR.Eval.CEK.Types , CoreBuiltinEnv , CoreCEKValue , CoreEvalResult - , EvalCapType(..) , CapBodyState(..) ) where @@ -380,35 +379,22 @@ data BuiltinCont (e :: RuntimeMode) (b :: K.Type) (i :: K.Type) -- ^ {closure} {accum} {rest} | ZipC (CanApply e b i) ([PactValue],[PactValue]) [PactValue] -- ^ - | PreSelectC TableValue (CanApply e b i) (Maybe [Field]) - -- ^
| FoldDbFilterC TableValue (CanApply e b i) (CanApply e b i) (RowKey, ObjectData PactValue) [RowKey] [(RowKey, PactValue)] -- ^
| FoldDbMapC TableValue (CanApply e b i) [(RowKey, PactValue)] [PactValue] -- ^
- | ReadC TableValue RowKey - -- ^
- | WriteC TableValue WriteType RowKey (ObjectData PactValue) - -- ^
- -- ^
- | WithDefaultReadC TableValue RowKey (ObjectData PactValue) (CanApply e b i) - -- ^
- | KeysC TableValue - -- ^ Table to apply `keys` to - -- | TxIdsC TableValue Integer + -- | WithDefaultReadC TableValue RowKey (ObjectData PactValue) (CanApply e b i) -- -- ^
- -- | TxLogC TableValue Integer - -- -- ^
- -- | KeyLogC TableValue RowKey Integer - -- -- ^
- | CreateTableC TableValue - -- ^ - | EmitEventC (CapToken FullyQualifiedName PactValue) - -- ^ + -- | KeysC TableValue + -- -- ^ Table to apply `keys` to + -- | CreateTableC TableValue + -- -- ^ | DefineKeysetC KeySetName KeySet -- ^ | DefineNamespaceC Namespace @@ -556,10 +542,6 @@ data ContType | CTMt deriving (Show, Eq, Enum, Bounded) -data EvalCapType - = NormalCapEval - | TestCapEval - deriving (Show, Eq, Enum, Bounded) data CEKErrorHandler (e :: RuntimeMode) (b :: K.Type) (i :: K.Type) = CEKNoHandler diff --git a/pact/Pact/Core/IR/Eval/CEK/Utils.hs b/pact/Pact/Core/IR/Eval/CEK/Utils.hs index 29445e926..0ca0e3ae5 100644 --- a/pact/Pact/Core/IR/Eval/CEK/Utils.hs +++ b/pact/Pact/Core/IR/Eval/CEK/Utils.hs @@ -94,7 +94,7 @@ sysOnlyEnv e let newPactdb = PactDb { _pdbPurity = PSysOnly - , _pdbRead = read' + , _pdbRead = \dom k -> read' dom k , _pdbWrite = \_ _ _ _ -> dbOpDisallowed , _pdbKeys = const dbOpDisallowed , _pdbCreateUserTable = \_ -> dbOpDisallowed @@ -105,7 +105,7 @@ sysOnlyEnv e in set cePactDb newPactdb e where pdb = view cePactDb e - read' :: Domain k v b i -> k -> IO (Maybe v) + read' :: Domain k v b i -> k -> GasM b i (Maybe v) read' dom k = case dom of DUserTables _ -> dbOpDisallowed _ -> _pdbRead pdb dom k diff --git a/pact/Pact/Core/IR/Eval/CoreBuiltin.hs b/pact/Pact/Core/IR/Eval/CoreBuiltin.hs index 4e4d2bf6a..4cd42311f 100644 --- a/pact/Pact/Core/IR/Eval/CoreBuiltin.hs +++ b/pact/Pact/Core/IR/Eval/CoreBuiltin.hs @@ -65,6 +65,7 @@ import Pact.Core.Capabilities import Pact.Core.Namespace import Pact.Core.Gas import Pact.Core.Type +import Pact.Core.ModRefs #ifndef WITHOUT_CRYPTO import Pact.Core.Crypto.Pairing import Pact.Core.Crypto.Hash.Poseidon @@ -804,7 +805,7 @@ keysetRefGuard info b cont handler env = \case Left {} -> throwNativeExecutionError info b "incorrect keyset name format" Right ksn -> do let pdb = view cePactDb env - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Nothing -> throwExecutionError info (NoSuchKeySet ksn) Just _ -> returnCEKValue cont handler (VGuard (GKeySetRef ksn)) @@ -980,51 +981,88 @@ coreBind info b cont handler _env = \case -------------------------------------------------- createTable :: (IsBuiltin b) => NativeFunction e b i -createTable info b cont handler env = \case - [VTable tv] -> do +createTable info b cont handler (_cePactDb -> pdb) = \case + [VTable tv@(TableValue tn _ _)] -> do enforceTopLevelOnly info b - let cont' = BuiltinC env info (CreateTableC tv) cont - guardTable info cont' handler env tv GtCreateTable + guardTable info tv GtCreateTable + evalCreateUserTable info pdb tn + returnCEKValue cont handler (VString "TableCreated") args -> argsError info b args dbSelect :: (IsBuiltin b) => NativeFunction e b i dbSelect info b cont handler env = \case [VTable tv, VClosure clo] -> do - let cont' = BuiltinC env info (PreSelectC tv clo Nothing) cont - guardTable info cont' handler env tv GtSelect + preSelect tv clo Nothing [VTable tv, VList li, VClosure clo] -> do li' <- traverse (fmap Field . asString info b) (V.toList li) - let cont' = BuiltinC env info (PreSelectC tv clo (Just li')) cont - guardTable info cont' handler env tv GtSelect + preSelect tv clo (Just li') args -> argsError info b args + where + pdb = _cePactDb env + preSelect tv clo mfields = do + guardTable info tv GtSelect + liftGasM info (_pdbKeys pdb (tvToDomain tv)) >>= \case + k:ks -> liftGasM info (_pdbRead pdb (tvToDomain tv) k) >>= \case + Just (RowData r) -> do + let bf = SelectC tv clo (ObjectData r) ks [] mfields + cont' = BuiltinC env info bf cont + applyLam clo [VObject r] cont' handler + Nothing -> + failInvariant info (InvariantNoSuchKeyInTable (_tvName tv) k) + [] -> returnCEKValue cont handler (VList mempty) foldDb :: (IsBuiltin b) => NativeFunction e b i foldDb info b cont handler env = \case - [VTable tv, VClosure queryClo, VClosure consumer] -> do - let cont' = BuiltinC env info (PreFoldDbC tv queryClo consumer) cont - guardTable info cont' handler env tv GtSelect + [VTable tv, VClosure queryClo, VClosure consumerClo] -> do + -- let cont' = BuiltinC env info (PreFoldDbC tv queryClo consumer) cont + guardTable info tv GtSelect + let tblDomain = DUserTables (_tvName tv) + let pdb = _cePactDb env + -- Todo: keys gas + liftGasM info (_pdbKeys pdb tblDomain) >>= \case + rk@(RowKey raw):remaining' -> liftGasM info (_pdbRead pdb (tvToDomain tv) rk) >>= \case + Just (RowData row) -> do + let rdf = FoldDbFilterC tv queryClo consumerClo (rk, ObjectData row) remaining' [] + cont' = BuiltinC env info rdf cont + applyLam queryClo [VString raw, VObject row] cont' handler + Nothing -> + failInvariant info (InvariantNoSuchKeyInTable (_tvName tv) rk) + [] -> returnCEKValue cont handler (VList mempty) args -> argsError info b args dbRead :: (IsBuiltin b) => NativeFunction e b i dbRead info b cont handler env = \case [VTable tv, VString k] -> do - let cont' = BuiltinC env info (ReadC tv (RowKey k)) cont - guardTable info cont' handler env tv GtRead + guardTable info tv GtRead + let rowkey = RowKey k + liftGasM info (_pdbRead (_cePactDb env) (tvToDomain tv) rowkey) >>= \case + Just (RowData rdata) -> do + bytes <- sizeOf info SizeOfV0 rdata + chargeGasArgs info (GRead bytes) + returnCEKValue cont handler (VObject rdata) + Nothing -> + returnCEKError info cont handler $ + NoSuchObjectInDb (_tvName tv) rowkey args -> argsError info b args dbWithRead :: (IsBuiltin b) => NativeFunction e b i dbWithRead info b cont handler env = \case [VTable tv, VString k, VClosure clo] -> do - let cont1 = Fn clo env [] [] cont - let cont2 = BuiltinC env info (ReadC tv (RowKey k)) cont1 - guardTable info cont2 handler env tv GtWithRead + let cont' = Fn clo env [] [] cont + dbRead info b cont' handler env [VTable tv, VString k] args -> argsError info b args dbWithDefaultRead :: (IsBuiltin b) => NativeFunction e b i dbWithDefaultRead info b cont handler env = \case [VTable tv, VString k, VObject defaultObj, VClosure clo] -> do - let cont' = BuiltinC env info (WithDefaultReadC tv (RowKey k) (ObjectData defaultObj) clo) cont - guardTable info cont' handler env tv GtWithDefaultRead + -- let cont' = BuiltinC env info (WithDefaultReadC tv (RowKey k) (ObjectData defaultObj) clo) cont + guardTable info tv GtRead + liftGasM info (_pdbRead (_cePactDb env) (tvToDomain tv) (RowKey k)) >>= \case + Just (RowData rdata) -> do + bytes <- sizeOf info SizeOfV0 rdata + chargeGasArgs info (GRead bytes) + applyLam clo [VObject rdata] cont handler + Nothing -> applyLam clo [VObject defaultObj] cont handler args -> argsError info b args -- | Todo: schema checking here? Or only on writes? @@ -1036,9 +1074,17 @@ dbInsert = write' Insert write' :: (IsBuiltin b) => WriteType -> NativeFunction e b i write' wt info b cont handler env = \case - [VTable tv, VString key, VObject o] -> do - let cont' = BuiltinC env info (WriteC tv wt (RowKey key) (ObjectData o)) cont - guardTable info cont' handler env tv GtWrite + [VTable tv, VString key, VObject rv] -> do + guardTable info tv GtWrite + let check' = if wt == Update then checkPartialSchema else checkSchema + if check' rv (_tvSchema tv) then do + let rdata = RowData rv + rvSize <- sizeOf info SizeOfV0 rv + chargeGasArgs info (GWrite rvSize) + evalWrite info (_cePactDb env) wt (tvToDomain tv) (RowKey key) rdata + returnCEKValue cont handler (VString "Write succeeded") + else + throwExecutionError info (WriteValueDidNotMatchSchema (_tvSchema tv) (ObjectData rv)) args -> argsError info b args dbUpdate :: (IsBuiltin b) => NativeFunction e b i @@ -1047,8 +1093,10 @@ dbUpdate = write' Update dbKeys :: (IsBuiltin b) => NativeFunction e b i dbKeys info b cont handler env = \case [VTable tv] -> do - let cont' = BuiltinC env info (KeysC tv) cont - guardTable info cont' handler env tv GtKeys + guardTable info tv GtKeys + ks <- liftGasM info (_pdbKeys (_cePactDb env) (tvToDomain tv)) + let li = V.fromList (PString . _rowKey <$> ks) + returnCEKValue cont handler (VList li) args -> argsError info b args defineKeySet' @@ -1072,7 +1120,7 @@ defineKeySet' info cont handler env ksname newKs = do chargeGasArgs info (GWrite newKsSize) evalWrite info pdb Write DKeySets ksn newKs returnCEKValue cont handler (VString "Keyset write success") - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just oldKs -> do let cont' = BuiltinC env info (DefineKeysetC ksn newKs) cont isKeysetInSigs info cont' handler env oldKs @@ -1128,10 +1176,9 @@ installCapability info b cont handler env = \case args -> argsError info b args coreEmitEvent :: (IsBuiltin b) => NativeFunction e b i -coreEmitEvent info b cont handler env = \case +coreEmitEvent info b cont handler _env = \case [VCapToken ct@(CapToken fqn _)] -> do - let cont' = BuiltinC env info (EmitEventC ct) cont - guardForModuleCall info cont' handler env (_fqModule fqn) $ do + guardForModuleCall info (_fqModule fqn) $ do -- Todo: this code is repeated in the EmitEventFrame code d <- getDefCap info fqn enforceMeta (_dcapMeta d) @@ -1452,12 +1499,12 @@ days info b cont handler _env = \case args -> argsError info b args describeModule :: (IsBuiltin b) => NativeFunction e b i -describeModule info b cont handler env = \case +describeModule info b cont handler _env = \case [VString s] -> case parseModuleName s of Just mname -> do enforceTopLevelOnly info b checkNonLocalAllowed info b - getModuleData info (view cePactDb env) mname >>= \case + getModuleData info mname >>= \case ModuleData m _ -> returnCEKValue cont handler $ VObject $ M.fromList $ fmap (over _1 Field) [ ("name", PString (renderModuleName (_mName m))) @@ -1491,7 +1538,7 @@ dbDescribeKeySet info b cont handler env = \case enforceTopLevelOnly info b case parseAnyKeysetName s of Right ksn -> do - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just ks -> returnCEKValue cont handler (VGuard (GKeyset ks)) Nothing -> @@ -1565,7 +1612,7 @@ coreNamespace info b cont handler env = \case returnCEKValue cont handler (VString "Namespace reset to root") else do chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case Just ns -> do size <- sizeOf info SizeOfV0 ns chargeGasArgs info $ GRead size @@ -1586,7 +1633,7 @@ coreDefineNamespace info b cont handler env = \case let nsn = NamespaceName n ns = Namespace nsn usrG adminG chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case -- G! -- https://static.wikia.nocookie.net/onepiece/images/5/52/Lao_G_Manga_Infobox.png/revision/latest?cb=20150405020446 -- Enforce the old guard @@ -1601,7 +1648,7 @@ coreDefineNamespace info b cont handler env = \case chargeGasArgs info (GWrite nsSize) evalWrite info pdb Write DNamespaces nsn ns returnCEKValue cont handler $ VString $ "Namespace defined: " <> n - SmartNamespacePolicy _ fun -> getModuleMemberWithHash info pdb fun >>= \case + SmartNamespacePolicy _ fun -> getModuleMemberWithHash info fun >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn fun mh) env let cont' = BuiltinC env info (DefineNamespaceC ns) cont @@ -1626,7 +1673,7 @@ coreDescribeNamespace info b cont handler _env = \case [VString n] -> do pdb <- viewEvalEnv eePactDb chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case Just existing@(Namespace _ usrG laoG) -> do size <- sizeOf info SizeOfV0 existing chargeGasArgs info $ GRead size @@ -1875,6 +1922,14 @@ coreHyperlaneEncodeTokenMessage info b cont handler _env = \case returnCEKValue cont handler (VString encoded) args -> argsError info b args +coreAcquireModuleAdmin :: (IsBuiltin b) => NativeFunction e b i +coreAcquireModuleAdmin info b cont handler env = \case + [VModRef m] -> do + let cont' = IgnoreValueC (PString ("Module admin for module " <> renderModuleName (_mrModule m) <> " acquired")) cont + mdl <- getModule info (_mrModule m) + acquireModuleAdmin info cont' handler env mdl + args -> argsError info b args + ----------------------------------- -- Builtin exports ----------------------------------- @@ -2029,3 +2084,4 @@ coreBuiltinRuntime = \case CoreHyperlaneMessageId -> coreHyperlaneMessageId CoreHyperlaneDecodeMessage -> coreHyperlaneDecodeTokenMessage CoreHyperlaneEncodeMessage -> coreHyperlaneEncodeTokenMessage + CoreAcquireModuleAdmin -> coreAcquireModuleAdmin diff --git a/pact/Pact/Core/IR/Eval/Direct/Evaluator.hs b/pact/Pact/Core/IR/Eval/Direct/Evaluator.hs index 5ee960fac..bc8cedb37 100644 --- a/pact/Pact/Core/IR/Eval/Direct/Evaluator.hs +++ b/pact/Pact/Core/IR/Eval/Direct/Evaluator.hs @@ -29,7 +29,8 @@ module Pact.Core.IR.Eval.Direct.Evaluator , evalCap , installCap , coreBuiltinRuntime - , enforcePactValue) where + , enforcePactValue + , evalWithinCap) where import Control.Lens hiding (op, from, to, parts) import Control.Monad @@ -153,6 +154,26 @@ eval purity benv term = do VPactValue pv -> pure pv _ -> throwExecutionError (view termInfo term) (EvalError "Evaluation did not reduce to a value") + +evalWithinCap + :: forall e b i + . (IsBuiltin b) + => i + -> Purity + -> BuiltinEnv e b i + -> CapToken QualifiedName PactValue + -> EvalTerm b i + -> EvalM e b i PactValue +evalWithinCap info purity benv (CapToken qualName vs) term = do + ee <- viewEvalEnv id + (_, mh) <- getDefCapQN info qualName + let ct = CapToken (qualNameToFqn qualName mh) vs + let cekEnv = envFromPurity purity (DirectEnv mempty (_eePactDb ee) benv (_eeDefPactStep ee) False) + evalCap (view termInfo term) cekEnv ct PopCapInvoke NormalCapEval term >>= \case + VPactValue pv -> pure pv + _ -> + throwExecutionError (view termInfo term) (EvalError "Evaluation did not reduce to a value") + interpretGuard :: forall e b i . (IsBuiltin b) @@ -225,12 +246,11 @@ evaluate env = \case failInvariant info (InvariantInvalidDefKind (defKind mname d) "in var position") Nothing -> failInvariant info (InvariantUnboundFreeVariable (FullyQualifiedName mname (_nName n) mh)) - NModRef m ifs -> case ifs of - [] -> throwExecutionError info (ModRefImplementsNoInterfaces m) - _ -> return (VModRef (ModRef m (S.fromList ifs))) + NModRef m ifs -> + return (VModRef (ModRef m (S.fromList ifs))) NDynRef (DynamicRef dArg i) -> case RAList.lookup (view ceLocal env) i of Just (VModRef mr) -> do - modRefHash <- _mHash <$> getModule info (view cePactDb env) (_mrModule mr) + modRefHash <- _mHash <$> getModule info (_mrModule mr) let nk = NTopLevel (_mrModule mr) modRefHash evaluate env (Var (Name dArg nk) info) Just _ -> throwExecutionError info (DynNameIsNotModRef dArg) @@ -299,7 +319,7 @@ evaluate env = \case enforceNotWithinDefcap info env "with-capability" rawCap <- enforceCapToken info =<< evaluate env cap let capModule = view (ctName . fqModule) rawCap - guardForModuleCall info env capModule $ pure () + guardForModuleCall info capModule $ pure () evalCap info env rawCap PopCapInvoke NormalCapEval body CreateUserGuard n uargs -> do fqn <- nameToFQN info env n @@ -521,34 +541,12 @@ nameToFQN info env (Name n nk) = case nk of NTopLevel mn mh -> pure (FullyQualifiedName mn n mh) NDynRef (DynamicRef dArg i) -> case RAList.lookup (view ceLocal env) i of Just (VModRef mr) -> do - md <- getModule info (view cePactDb env) (_mrModule mr) + md <- getModule info (_mrModule mr) pure (FullyQualifiedName (_mrModule mr) dArg (_mHash md)) Just _ -> throwExecutionError info (DynNameIsNotModRef n) Nothing -> failInvariant info (InvariantInvalidBoundVariable n) _ -> failInvariant info (InvariantInvalidBoundVariable n) -guardTable - :: (IsBuiltin b) - => i - -> DirectEnv e b i - -> TableValue - -> GuardTableOp - -> EvalM e b i () -guardTable i env (TableValue tn mh _) dbop = do - let mn = _tableModuleName tn - checkLocalBypass $ - guardForModuleCall i env mn $ do - mdl <- getModule i (view cePactDb env) mn - enforceBlessedHashes i mdl mh - where - checkLocalBypass notBypassed = do - enabled <- isExecutionFlagSet FlagAllowReadInLocal - case dbop of - GtWrite -> notBypassed - GtCreateTable -> notBypassed - _ | enabled -> return () - | otherwise -> notBypassed - -- Todo: should we typecheck / arity check here? createUserGuard @@ -677,7 +675,7 @@ sysOnlyEnv e in set cePactDb newPactdb e where pdb = view cePactDb e - read' :: Domain k v b i -> k -> IO (Maybe v) + read' :: Domain k v b i -> k -> GasM b i (Maybe v) read' dom k = case dom of DUserTables _ -> dbOpDisallowed _ -> _pdbRead pdb dom k @@ -878,21 +876,6 @@ enforceGuard info env g = case g of else throwUserRecoverableError info $ CapabilityPactGuardInvalidPactId curDpid dpid -guardForModuleCall - :: (IsBuiltin b) - => i - -> DirectEnv e b i - -> ModuleName - -> EvalM e b i () - -> EvalM e b i () -guardForModuleCall i env currMod onFound = - findCallingModule >>= \case - Just mn | mn == currMod -> onFound - _ -> do - mc <- use (esCaps . csModuleAdmin) - if S.member currMod mc then onFound - else getModule i (view cePactDb env) currMod >>= acquireModuleAdmin i env - -- | Acquires module admin for a known module -- NOTE: This function should only be called _after_ -- checking whether `esCaps . csModuleAdmin` for the particular @@ -921,7 +904,7 @@ acquireModuleAdminCapability -> EvalM e b i () acquireModuleAdminCapability i env mname = do sc <- S.member mname <$> use (esCaps . csModuleAdmin) - unless sc $ getModule i (_cePactDb env) mname >>= acquireModuleAdmin i env + unless sc $ getModule i mname >>= acquireModuleAdmin i env runUserGuard @@ -931,7 +914,7 @@ runUserGuard -> UserGuard QualifiedName PactValue -> EvalM e b i Bool runUserGuard info env (UserGuard qn args) = - getModuleMemberWithHash info (_cePactDb env) qn >>= \case + getModuleMemberWithHash info qn >>= \case (Dfun d, mh) -> do when (length (_dfunArgs d) /= length args) $ throwExecutionError info CannotApplyPartialClosure let env' = sysOnlyEnv env @@ -985,8 +968,7 @@ isKeysetInSigs info env (KeySet kskeys ksPred) = do CustomPredicate n -> runCustomPred matched n runCustomPred matched = \case TQN qn -> do - pdb <- viewEvalEnv eePactDb - getModuleMemberWithHash info pdb qn >>= \case + getModuleMemberWithHash info qn >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn qn mh) env p <- enforceBool info =<< applyLam (C clo) [VInteger (fromIntegral count), VInteger (fromIntegral matched)] @@ -1013,7 +995,7 @@ isKeysetNameInSigs -> EvalM e b i Bool isKeysetNameInSigs info env ksn = do pdb <- viewEvalEnv eePactDb - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just ks -> isKeysetInSigs info env ks Nothing -> throwExecutionError info (NoSuchKeySet ksn) @@ -1150,7 +1132,7 @@ applyPact -> EvalM e b i (EvalValue e b i) applyPact i pc ps cenv nested = use esDefPactExec >>= \case Just pe -> throwExecutionError i (MultipleOrNestedDefPactExecFound pe) - Nothing -> getModuleMemberWithHash i (_cePactDb cenv) (pc ^. pcName) >>= \case + Nothing -> getModuleMemberWithHash i (pc ^. pcName) >>= \case (DPact defPact, mh) -> do let nSteps = NE.length (_dpSteps defPact) @@ -1210,7 +1192,7 @@ applyNestedPact applyNestedPact i pc ps cenv = use esDefPactExec >>= \case Nothing -> failInvariant i $ InvariantPactExecNotInEnv Nothing - Just pe -> getModuleMemberWithHash i (_cePactDb cenv) (pc ^. pcName) >>= \case + Just pe -> getModuleMemberWithHash i (pc ^. pcName) >>= \case (DPact defPact, mh) -> do step <- maybe (throwExecutionError i (InvalidDefPactStepSupplied ps (_peStepCount pe))) pure $ _dpSteps defPact ^? ix (ps ^. psStep) @@ -1278,7 +1260,7 @@ resumePact i env crossChainContinuation = viewEvalEnv eeDefPactStep >>= \case Nothing -> throwExecutionError i DefPactStepNotInEnvironment Just ps -> do pdb <- viewEvalEnv eePactDb - dbState <- liftDbFunction i (_pdbRead pdb DDefPacts (_psDefPactId ps)) + dbState <- liftGasM i (_pdbRead pdb DDefPacts (_psDefPactId ps)) case (dbState, crossChainContinuation) of (Just Nothing, _) -> throwExecutionError i (DefPactAlreadyCompleted ps) (Nothing, Nothing) -> throwExecutionError i (NoPreviousDefPactExecutionFound ps) @@ -2077,7 +2059,7 @@ keysetRefGuard info b env = \case Left {} -> throwNativeExecutionError info b "incorrect keyset name format" Right ksn -> do let pdb = view cePactDb env - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Nothing -> throwExecutionError info (NoSuchKeySet ksn) Just _ -> return (VGuard (GKeySetRef ksn)) args -> argsError info b args @@ -2254,7 +2236,7 @@ createTable info b env = \case [VTable tv] -> do enforceTopLevelOnly info b let pdb = _cePactDb env - guardTable info env tv GtCreateTable + guardTable info tv GtCreateTable evalCreateUserTable info pdb (_tvName tv) return (VString "TableCreated") args -> argsError info b args @@ -2270,14 +2252,14 @@ dbSelect info b env = \case where pdb = _cePactDb env selectRead tv clo mf = do - guardTable info env tv GtSelect - ks <- liftDbFunction info (_pdbKeys pdb (tvToDomain tv)) + guardTable info tv GtSelect + ks <- liftGasM info (_pdbKeys pdb (tvToDomain tv)) VList . V.fromList . fmap (PObject . mRestrictFields mf) . catMaybes <$> traverse go ks where mRestrictFields = maybe id (\fields -> flip M.restrictKeys (S.fromList fields)) go k = - liftDbFunction info (_pdbRead pdb (tvToDomain tv) k) >>= \case + liftGasM info (_pdbRead pdb (tvToDomain tv) k) >>= \case Just (RowData r) -> do cond <- enforceBool info =<< applyLam clo [VObject r] if cond then pure $ Just r @@ -2289,14 +2271,12 @@ dbSelect info b env = \case foldDb :: (IsBuiltin b) => NativeFunction e b i foldDb info b env = \case [VTable tv, VClosure queryClo, VClosure consumer] -> do - -- let cont' = BuiltinC env info (PreFoldDbC tv queryClo consumer) cont - -- guardTable info cont' handler env tv GtSelect - guardTable info env tv GtSelect - keys <- liftDbFunction info (_pdbKeys pdb (tvToDomain tv)) + guardTable info tv GtSelect + keys <- liftGasM info (_pdbKeys pdb (tvToDomain tv)) VList . V.fromList . catMaybes <$> traverse go keys where go rk@(RowKey raw) = do - liftDbFunction info (_pdbRead pdb (tvToDomain tv) rk) >>= \case + liftGasM info (_pdbRead pdb (tvToDomain tv) rk) >>= \case Just (RowData row) -> do qryCond <- enforceBool info =<< applyLam queryClo [VString raw, VObject row] if qryCond then do @@ -2316,7 +2296,7 @@ readUserTable -> RowKey -> EvalM e b i RowData readUserTable info env tv rk = do - liftDbFunction info (_pdbRead (_cePactDb env) (tvToDomain tv) rk) >>= \case + liftGasM info (_pdbRead (_cePactDb env) (tvToDomain tv) rk) >>= \case Just rd -> return rd Nothing -> throwUserRecoverableError info $ NoSuchObjectInDb (_tvName tv) rk @@ -2324,24 +2304,28 @@ readUserTable info env tv rk = do dbRead :: (IsBuiltin b) => NativeFunction e b i dbRead info b env = \case [VTable tv, VString rk] -> do - guardTable info env tv GtRead - VObject . _unRowData <$> readUserTable info env tv (RowKey rk) + guardTable info tv GtRead + RowData rdata <- readUserTable info env tv (RowKey rk) + bytes <- sizeOf info SizeOfV0 rdata + chargeGasArgs info (GRead bytes) + return (VObject rdata) args -> argsError info b args dbWithRead :: (IsBuiltin b) => NativeFunction e b i dbWithRead info b env = \case [VTable tv, VString rk, VClosure clo] -> do - guardTable info env tv GtRead - RowData o <- readUserTable info env tv (RowKey rk) - applyLam clo [VObject o] >>= enforcePactValue' info + v <- dbRead info b env [VTable tv, VString rk] + applyLam clo [v] >>= enforcePactValue' info args -> argsError info b args dbWithDefaultRead :: (IsBuiltin b) => NativeFunction e b i dbWithDefaultRead info b env = \case [VTable tv, VString rk, VObject defaultObj, VClosure clo] -> do - guardTable info env tv GtWithDefaultRead - liftDbFunction info (_pdbRead (_cePactDb env) (tvToDomain tv) (RowKey rk)) >>= \case - Just (RowData o) -> + guardTable info tv GtRead + liftGasM info (_pdbRead (_cePactDb env) (tvToDomain tv) (RowKey rk)) >>= \case + Just (RowData o) -> do + bytes <- sizeOf info SizeOfV0 o + chargeGasArgs info (GRead bytes) applyLam clo [VObject o] >>= enforcePactValue' info Nothing -> applyLam clo [VObject defaultObj] >>= enforcePactValue' info @@ -2357,7 +2341,7 @@ dbInsert = write' Insert write' :: (IsBuiltin b) => WriteType -> NativeFunction e b i write' wt info b env = \case [VTable tv, VString key, VObject rv] -> do - guardTable info env tv GtWrite + guardTable info tv GtWrite let pdb = _cePactDb env let check' = if wt == Update then checkPartialSchema else checkSchema if check' rv (_tvSchema tv) then do @@ -2375,9 +2359,9 @@ dbUpdate = write' Update dbKeys :: (IsBuiltin b) => NativeFunction e b i dbKeys info b env = \case [VTable tv] -> do - guardTable info env tv GtKeys + guardTable info tv GtKeys let pdb = _cePactDb env - ks <- liftDbFunction info (_pdbKeys pdb (tvToDomain tv)) + ks <- liftGasM info (_pdbKeys pdb (tvToDomain tv)) let li = V.fromList (PString . _rowKey <$> ks) return (VList li) -- let cont' = BuiltinC env info (KeysC tv) cont @@ -2402,7 +2386,7 @@ defineKeySet' info env ksname newKs = do chargeGasArgs info (GWrite newKsSize) evalWrite info pdb Write DKeySets ksn newKs return (VString "Keyset write success") - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just oldKs -> do _ <- isKeysetInSigs info env oldKs writeKs @@ -2456,10 +2440,10 @@ installCapability info b env = \case args -> argsError info b args coreEmitEvent :: (IsBuiltin b) => NativeFunction e b i -coreEmitEvent info b env = \case +coreEmitEvent info b _env = \case [VCapToken ct@(CapToken fqn _)] -> do -- let cont' = BuiltinC env info (EmitEventC ct) cont - guardForModuleCall info env (_fqModule fqn) $ return () + guardForModuleCall info (_fqModule fqn) $ return () d <- getDefCap info fqn enforceMeta (_dcapMeta d) emitCapability info ct @@ -2775,12 +2759,12 @@ days info b _env = \case args -> argsError info b args describeModule :: (IsBuiltin b) => NativeFunction e b i -describeModule info b env = \case +describeModule info b _env = \case [VString s] -> case parseModuleName s of Just mname -> do enforceTopLevelOnly info b checkNonLocalAllowed info b - getModuleData info (view cePactDb env) mname >>= \case + getModuleData info mname >>= \case ModuleData m _ -> return $ VObject $ M.fromList $ fmap (over _1 Field) [ ("name", PString (renderModuleName (_mName m))) @@ -2813,7 +2797,7 @@ dbDescribeKeySet info b env = \case enforceTopLevelOnly info b case parseAnyKeysetName s of Right ksn -> do - liftDbFunction info (_pdbRead pdb DKeySets ksn) >>= \case + liftGasM info (_pdbRead pdb DKeySets ksn) >>= \case Just ks -> return (VGuard (GKeyset ks)) Nothing -> @@ -2888,7 +2872,7 @@ coreNamespace info b env = \case return (VString "Namespace reset to root") else do chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case Just ns -> do size <- sizeOf info SizeOfV0 ns chargeGasArgs info $ GRead size @@ -2908,7 +2892,7 @@ coreDefineNamespace info b env = \case let nsn = NamespaceName n ns = Namespace nsn usrG adminG chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case -- G! -- https://static.wikia.nocookie.net/onepiece/images/5/52/Lao_G_Manga_Infobox.png/revision/latest?cb=20150405020446 -- Enforce the old guard @@ -2923,7 +2907,7 @@ coreDefineNamespace info b env = \case chargeGasArgs info (GWrite nsSize) evalWrite info pdb Write DNamespaces nsn ns return $ VString $ "Namespace defined: " <> n - SmartNamespacePolicy _ fun -> getModuleMemberWithHash info pdb fun >>= \case + SmartNamespacePolicy _ fun -> getModuleMemberWithHash info fun >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn fun mh) env allow <- enforceBool info =<< applyLam (C clo) [VString n, VGuard adminG] @@ -2955,7 +2939,7 @@ coreDescribeNamespace info b _env = \case [VString n] -> do pdb <- viewEvalEnv eePactDb chargeGasArgs info $ GRead $ fromIntegral $ T.length n - liftDbFunction info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case + liftGasM info (_pdbRead pdb DNamespaces (NamespaceName n)) >>= \case Just existing@(Namespace _ usrG laoG) -> do size <- sizeOf info SizeOfV0 existing chargeGasArgs info $ GRead size @@ -3206,6 +3190,14 @@ coreHyperlaneEncodeTokenMessage info b _env = \case return (VString encoded) args -> argsError info b args +coreAcquireModuleAdmin :: (IsBuiltin b) => NativeFunction e b i +coreAcquireModuleAdmin info b env = \case + [VModRef m] -> do + let msg = VString ("Module admin for module " <> renderModuleName (_mrModule m) <> " acquired") + acquireModuleAdminCapability info env (_mrModule m) + return msg + args -> argsError info b args + ----------------------------------- -- Builtin exports @@ -3372,3 +3364,4 @@ coreBuiltinRuntime = CoreHyperlaneMessageId -> coreHyperlaneMessageId CoreHyperlaneDecodeMessage -> coreHyperlaneDecodeTokenMessage CoreHyperlaneEncodeMessage -> coreHyperlaneEncodeTokenMessage + CoreAcquireModuleAdmin -> coreAcquireModuleAdmin diff --git a/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs b/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs index df57fa5c6..d685c970d 100644 --- a/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs +++ b/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs @@ -386,11 +386,10 @@ envExecConfig info b _env = \case envNamespacePolicy :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo envNamespacePolicy info b _env = \case [VBool allowRoot, VClosure (C clo)] -> do - pdb <- viewEvalEnv eePactDb let qn = fqnToQualName (_cloFqName clo) when (_cloArity clo /= 2) $ throwNativeExecutionError info b "Namespace manager function has invalid argument length" - getModuleMember info pdb qn >>= \case + getModuleMember info qn >>= \case Dfun _ -> do let nsp = SmartNamespacePolicy allowRoot qn replEvalEnv . eeNamespacePolicy .== nsp diff --git a/pact/Pact/Core/IR/Eval/Direct/Types.hs b/pact/Pact/Core/IR/Eval/Direct/Types.hs index 4b6b5267b..04d818e96 100644 --- a/pact/Pact/Core/IR/Eval/Direct/Types.hs +++ b/pact/Pact/Core/IR/Eval/Direct/Types.hs @@ -47,7 +47,6 @@ module Pact.Core.IR.Eval.Direct.Types , pattern VPartialClosure , pattern VDefPactClosure , CapPopState(..) - , EvalCapType(..) , NativeFunction , BuiltinEnv , toArgTypeError @@ -310,10 +309,6 @@ data CapPopState | PopCapInvoke deriving (Eq, Show, Generic) -data EvalCapType - = NormalCapEval - | TestCapEval - deriving (Show, Eq, Enum, Bounded) instance (NFData b, NFData i) => NFData (CanApply e b i) instance (NFData b, NFData i) => NFData (NativeFn e b i) diff --git a/pact/Pact/Core/IR/Eval/Runtime/Types.hs b/pact/Pact/Core/IR/Eval/Runtime/Types.hs index 9bdc778cb..d0ab3155e 100644 --- a/pact/Pact/Core/IR/Eval/Runtime/Types.hs +++ b/pact/Pact/Core/IR/Eval/Runtime/Types.hs @@ -14,7 +14,8 @@ module Pact.Core.IR.Eval.Runtime.Types ( TableValue(..) - , ErrorState(..)) where + , ErrorState(..) + , EvalCapType(..)) where @@ -47,3 +48,8 @@ data ErrorState i deriving (Show, Generic) instance NFData i => NFData (ErrorState i) + +data EvalCapType + = NormalCapEval + | TestCapEval + deriving (Show, Eq, Enum, Bounded) diff --git a/pact/Pact/Core/IR/Eval/Runtime/Utils.hs b/pact/Pact/Core/IR/Eval/Runtime/Utils.hs index 4d9718367..49e12b472 100644 --- a/pact/Pact/Core/IR/Eval/Runtime/Utils.hs +++ b/pact/Pact/Core/IR/Eval/Runtime/Utils.hs @@ -52,6 +52,8 @@ module Pact.Core.IR.Eval.Runtime.Utils , renderPactValue , createPrincipalForGuard , createEnumerateList + , guardForModuleCall + , guardTable ) where import Control.Lens hiding (from, to) @@ -61,7 +63,7 @@ import Data.IORef import Data.Monoid import Data.Vector(Vector) import Data.Foldable(find, toList) -import Data.Maybe(listToMaybe, maybeToList) +import Data.Maybe(maybeToList) import Data.Text(Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T @@ -136,10 +138,30 @@ getDefCap info fqn = lookupFqName fqn >>= \case Just _ -> failInvariant info (InvariantExpectedDefCap fqn) _ -> failInvariant info (InvariantUnboundFreeVariable fqn) +guardTable + :: () + => i + -> TableValue + -> GuardTableOp + -> EvalM e b i () +guardTable i (TableValue tn mh _) dbop = do + let mn = _tableModuleName tn + checkLocalBypass $ + guardForModuleCall i mn $ do + mdl <- getModule i mn + enforceBlessedHashes i mdl mh + where + checkLocalBypass notBypassed = do + enabled <- isExecutionFlagSet FlagAllowReadInLocal + case dbop of + GtWrite -> notBypassed + GtCreateTable -> notBypassed + _ | enabled -> pure () + | otherwise -> notBypassed + getDefCapQN :: i -> QualifiedName -> EvalM e b i (EvalDefCap b i, ModuleHash) getDefCapQN info qn = do - pdb <- viewEvalEnv eePactDb - getModuleMemberWithHash info pdb qn >>= \case + getModuleMemberWithHash info qn >>= \case (DCap d, mh) -> pure (d, mh) (_, mh) -> failInvariant info (InvariantUnboundFreeVariable (qualNameToFqn qn mh)) @@ -174,8 +196,9 @@ pvToArgTypeError = \case findCallingModule :: EvalM e b i (Maybe ModuleName) findCallingModule = do - stack <- use esStack - pure $ listToMaybe $ fmap (_fqModule . _sfName) stack + use esStack >>= \case + (StackFrame fqn _ _ _) : _ -> pure (Just (_fqModule fqn)) + _ -> pure Nothing calledByModule :: ModuleName @@ -196,9 +219,7 @@ failInvariant i reason = getCallingModule :: i -> EvalM e b i (EvalModule b i) getCallingModule info = findCallingModule >>= \case - Just mn -> do - pdb <- viewEvalEnv eePactDb - getModule info pdb mn + Just mn -> getModule info mn Nothing -> throwExecutionError info (EvalError "no module call in stack") @@ -361,6 +382,21 @@ enforceBlessedHashes info md mh | mh `S.member` _mBlessed md = return () | otherwise = throwExecutionError info (HashNotBlessed (_mName md) mh) +guardForModuleCall + :: () + => i + -> ModuleName + -> EvalM e b i a + -> EvalM e b i a +guardForModuleCall i currMod onFound = + findCallingModule >>= \case + Just mn | mn == currMod -> onFound + _ -> do + mc <- use (esCaps . csModuleAdmin) + if S.member currMod mc then onFound + else + throwExecutionError i (ModuleAdminNotAcquired currMod) + enforceStackTopIsDefcap :: IsBuiltin b => i diff --git a/pact/Pact/Core/Interpreter.hs b/pact/Pact/Core/Interpreter.hs index 3cf23c4cd..e48eec2c9 100644 --- a/pact/Pact/Core/Interpreter.hs +++ b/pact/Pact/Core/Interpreter.hs @@ -9,6 +9,7 @@ import Pact.Core.IR.Term import Pact.Core.PactValue import Pact.Core.Environment import Pact.Core.DefPacts.Types +import Pact.Core.Capabilities -- | Our Interpreter abstraction for -- working with different pact interpreters. @@ -17,4 +18,5 @@ data Interpreter e b i { interpretGuard :: !(i -> Guard QualifiedName PactValue -> EvalM e b i PactValue) , eval :: !(Purity -> EvalTerm b i -> EvalM e b i PactValue) , resumePact :: !(i -> Maybe DefPactExec -> EvalM e b i PactValue) + , evalWithCapability :: !(i -> Purity -> CapToken QualifiedName PactValue -> EvalTerm b i -> EvalM e b i PactValue) } diff --git a/pact/Pact/Core/Legacy/LegacyPactValue.hs b/pact/Pact/Core/Legacy/LegacyPactValue.hs index dcea3202b..cb52a8a11 100644 --- a/pact/Pact/Core/Legacy/LegacyPactValue.hs +++ b/pact/Pact/Core/Legacy/LegacyPactValue.hs @@ -3,13 +3,15 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Pact.Core.Legacy.LegacyPactValue - (roundtripPactValue + ( roundtripPactValue , Legacy(..) + , decodeLegacy ) where import Control.Applicative import Data.Aeson import Data.String (IsString (..)) +import Data.Text(Text) import qualified Pact.JSON.Encode as J @@ -25,8 +27,13 @@ import Pact.Core.ModRefs import Pact.Core.PactValue import Pact.Core.Legacy.LegacyCodec import Pact.Core.StableEncoding +import Pact.Core.Persistence.Types(RowData(..)) import Data.List +import Data.ByteString (ByteString) +decodeLegacy :: FromJSON (Legacy v) => ByteString -> Maybe v +decodeLegacy = fmap _unLegacy . A.decodeStrict +{-# INLINE decodeLegacy #-} newtype Legacy a = Legacy { _unLegacy :: a } @@ -88,11 +95,13 @@ instance FromJSON (Legacy (CapToken QualifiedName PactValue)) where legacyName <- o .: "name" legacyArgs <- o .: "args" pure $ Legacy $ CapToken (_unLegacy legacyName) (_unLegacy <$> legacyArgs) + {-# INLINE parseJSON #-} instance FromJSON (Legacy QualifiedName) where parseJSON = withText "QualifiedName" $ \t -> case parseQualifiedName t of Just qn -> pure (Legacy qn) _ -> fail "could not parse qualified name" + {-# INLINE parseJSON #-} instance FromJSON (Legacy ModuleName) where parseJSON = withObject "module name" $ \o -> @@ -100,12 +109,14 @@ instance FromJSON (Legacy ModuleName) where ModuleName <$> (o .: "name") <*> (fmap NamespaceName <$> (o .: "namespace")) + {-# INLINE parseJSON #-} -instance FromJSON (Legacy (UserGuard QualifiedName PactValue)) where +instance FromJSON (Legacy v) => FromJSON (Legacy (UserGuard QualifiedName v)) where parseJSON = withObject "UserGuard" $ \o -> Legacy <$> (UserGuard <$> (_unLegacy <$> o .: "fun") <*> (fmap _unLegacy <$> o .: "args")) + {-# INLINE parseJSON #-} instance FromJSON (Legacy KeySetName) where parseJSON v = @@ -116,8 +127,9 @@ instance FromJSON (Legacy KeySetName) where withObject "KeySetName" $ \o -> KeySetName <$> o .: "ksn" <*> (fmap NamespaceName <$> o .:? "ns") + {-# INLINE parseJSON #-} -instance FromJSON (Legacy (Guard QualifiedName PactValue)) where +instance FromJSON (Legacy v) => FromJSON (Legacy (Guard QualifiedName v)) where parseJSON v = case props v of [GuardKeys, GuardPred] -> Legacy . GKeyset . _unLegacy <$> parseJSON v [GuardKeysetref] -> flip (withObject "KeySetRef") v $ \o -> @@ -143,6 +155,7 @@ instance FromJSON (Legacy Literal) where -- (LTime <$> decoder timeCodec o) <|> (Legacy . LDecimal <$> decoder decimalCodec o) parseJSON _t = fail "Literal parse failed" + {-# INLINE parseJSON #-} instance FromJSON (Legacy KSPredicate) where parseJSON = withText "kspredfun" $ \case @@ -151,27 +164,30 @@ instance FromJSON (Legacy KSPredicate) where "keys-2" -> pure $ Legacy Keys2 t | Just pn <- parseParsedTyName t -> pure $ Legacy (CustomPredicate pn) | otherwise -> fail "invalid keyset predicate" + {-# INLINE parseJSON #-} instance FromJSON (Legacy KeySet) where - parseJSON v = - Legacy <$> (withObject "KeySet" keyListPred v <|> keyListOnly) - where + parseJSON v = + Legacy <$> (withObject "KeySet" keyListPred v <|> keyListOnly) + where - keyListPred o = KeySet - <$> (S.fromList . fmap PublicKeyText <$> (o .: "keys")) - <*> (maybe KeysAll _unLegacy <$> o .:? "pred") + keyListPred o = KeySet + <$> (S.fromList . fmap PublicKeyText <$> (o .: "keys")) + <*> (maybe KeysAll _unLegacy <$> o .:? "pred") - keyListOnly = KeySet - <$> (S.fromList . fmap PublicKeyText <$> parseJSON v) - <*> pure KeysAll + keyListOnly = KeySet + <$> (S.fromList . fmap PublicKeyText <$> parseJSON v) + <*> pure KeysAll + {-# INLINE parseJSON #-} instance FromJSON (Legacy ModRef) where parseJSON = withObject "ModRef" $ \o -> fmap Legacy $ ModRef <$> (_unLegacy <$> o .: "refName") <*> (S.fromList . fmap _unLegacy <$> o .: "refSpec") + {-# INLINE parseJSON #-} instance FromJSON (Legacy PactValue) where parseJSON v = fmap Legacy $ @@ -181,12 +197,14 @@ instance FromJSON (Legacy PactValue) where (PModRef . _unLegacy <$> parseJSON v) <|> (PTime <$> decoder timeCodec v) <|> (PObject . fmap _unLegacy <$> parseJSON v) + {-# INLINE parseJSON #-} instance FromJSON (Legacy ModuleGuard) where parseJSON = withObject "ModuleGuard" $ \o -> fmap Legacy $ ModuleGuard <$> (_unLegacy <$> o .: "moduleName") <*> (o .: "name") + {-# INLINE parseJSON #-} instance FromJSON (Legacy DefPactGuard) where parseJSON = withObject "DefPactGuard" $ \o -> do @@ -194,15 +212,52 @@ instance FromJSON (Legacy DefPactGuard) where DefPactGuard <$> (DefPactId <$> o .: "pactId") <*> o .: "name" + {-# INLINE parseJSON #-} -instance FromJSON (Legacy (CapabilityGuard QualifiedName PactValue)) where +instance FromJSON (Legacy v) => FromJSON (Legacy (CapabilityGuard QualifiedName v)) where parseJSON = withObject "CapabilityGuard" $ \o -> fmap Legacy $ CapabilityGuard <$> (_unLegacy <$> o .: "cgName") <*> (fmap _unLegacy <$> o .: "cgArgs") <*> (fmap DefPactId <$> o .: "cgPactId") + {-# INLINE parseJSON #-} roundtripPactValue :: PactValue -> Maybe PactValue roundtripPactValue pv = _unLegacy <$> A.decodeStrict' (encodeStable pv) + +instance FromJSON (Legacy RowData) where + parseJSON v = + parseVersioned v <|> + -- note: Parsing into `OldPactValue` here defaults to the code used in + -- the old FromJSON instance for PactValue, prior to the fix of moving + -- the `PModRef` parsing before PObject + Legacy . RowData . fmap _unLegacy <$> parseJSON v + where + parseVersioned = withObject "RowData" $ \o -> Legacy . RowData + <$> (fmap (_unRowDataValue._unLegacy) <$> o .: "$d") + {-# INLINE parseJSON #-} + +newtype RowDataValue + = RowDataValue { _unRowDataValue :: PactValue } + deriving (Show, Eq) + +instance FromJSON (Legacy RowDataValue) where + parseJSON v1 = + (Legacy . RowDataValue . PLiteral . _unLegacy <$> parseJSON v1) <|> + (Legacy . RowDataValue . PList . fmap (_unRowDataValue . _unLegacy) <$> parseJSON v1) <|> + parseTagged v1 + where + parseTagged = withObject "tagged RowData" $ \o -> do + (t :: Text) <- o .: "$t" + val <- o .: "$v" + case t of + "o" -> Legacy . RowDataValue . PObject . fmap (_unRowDataValue . _unLegacy) <$> parseJSON val + "g" -> Legacy . RowDataValue . PGuard . fmap (_unRowDataValue) . _unLegacy <$> parseJSON val + "m" -> Legacy . RowDataValue . PModRef <$> parseMR val + _ -> fail "tagged RowData" + parseMR = withObject "tagged ModRef" $ \o -> ModRef + <$> (fmap _unLegacy $ o .: "refName") + <*> (maybe mempty (S.fromList . fmap _unLegacy) <$> o .: "refSpec") + {-# INLINE parseJSON #-} diff --git a/pact/Pact/Core/Persistence/MockPersistence.hs b/pact/Pact/Core/Persistence/MockPersistence.hs index 027be4671..502173a91 100644 --- a/pact/Pact/Core/Persistence/MockPersistence.hs +++ b/pact/Pact/Core/Persistence/MockPersistence.hs @@ -200,30 +200,30 @@ mockPactDb serial = do keys :: PactTables b i -> Domain k v b i - -> IO [k] + -> GasM b i [k] keys PactTables{..} d = case d of DKeySets -> do - MockSysTable r <- readIORef ptKeysets + MockSysTable r <- liftIO $ readIORef ptKeysets -- Note: the parser only fails on null input, so -- if this ever fails, then somehow the null key got into the keysets. -- this is benign. let getKeysetName = fromMaybe (KeySetName "" Nothing) . rightToMaybe . parseAnyKeysetName return $ getKeysetName . _unRender <$> M.keys r DModules -> do - MockSysTable r <- readIORef ptModules + MockSysTable r <- liftIO $ readIORef ptModules let getModuleName = parseModuleName . _unRender return $ catMaybes $ getModuleName <$> M.keys r DUserTables tbl -> do - MockUserTable r <- readIORef ptUser + MockUserTable r <- liftIO $ readIORef ptUser let tblName = renderTableName tbl case M.lookup tblName r of Just t -> return (M.keys t) Nothing -> throwIO (Errors.NoSuchTable tbl) DDefPacts -> do - MockSysTable r <- readIORef ptDefPact + MockSysTable r <- liftIO $ readIORef ptDefPact return $ DefPactId . _unRender <$> M.keys r DNamespaces -> do - MockSysTable r <- readIORef ptNamespaces + MockSysTable r <- liftIO $ readIORef ptNamespaces pure $ NamespaceName . _unRender <$> M.keys r createUsrTable @@ -247,7 +247,7 @@ mockPactDb serial = do . PactTables b i -> Domain k v b i -> k - -> IO (Maybe v) + -> GasM b i (Maybe v) read' PactTables{..} domain k = case domain of DKeySets -> readSysTable ptKeysets k (Rendered . renderKeySetName) _decodeKeySet DModules -> readSysTable ptModules k (Rendered . renderModuleName) _decodeModuleData @@ -277,9 +277,14 @@ mockPactDb serial = do DDefPacts -> liftIO $ liftIO $ writeSysTable pt domain k v (Rendered . _defPactId) _encodeDefPactExec DNamespaces -> liftIO $ liftIO $ writeSysTable pt domain k v (Rendered . _namespaceName) _encodeNamespace + readRowData + :: IORef MockUserTable + -> TableName + -> RowKey + -> GasM b i (Maybe RowData) readRowData ref tbl k = do let tblName = renderTableName tbl - mt@(MockUserTable usrTables) <- readIORef ref + mt@(MockUserTable usrTables) <- liftIO $ readIORef ref checkTable tblName tbl mt case usrTables ^? ix tblName . ix k of Just bs -> case _decodeRowData serial bs of @@ -332,9 +337,9 @@ mockPactDb serial = do -> k -> (k -> Rendered k) -> (PactSerialise b i -> ByteString -> Maybe (Document v)) - -> IO (Maybe v) + -> GasM b i (Maybe v) readSysTable ref rowkey renderKey decode = do - MockSysTable m <- readIORef ref + MockSysTable m <- liftIO $ readIORef ref case M.lookup (renderKey rowkey) m of Just bs -> case decode serial bs of Just rd -> pure (Just (view document rd)) diff --git a/pact/Pact/Core/Persistence/SQLite.hs b/pact/Pact/Core/Persistence/SQLite.hs index 981577317..60b24d425 100644 --- a/pact/Pact/Core/Persistence/SQLite.hs +++ b/pact/Pact/Core/Persistence/SQLite.hs @@ -171,8 +171,8 @@ initializePactDb serial db = do , _pdbRollbackTx = rollbackTx db txLog }, stmtsCache) -readKeys :: forall k v b i. SQL.Database -> IORef StmtCache -> Domain k v b i -> IO [k] -readKeys _db stmtCache = \case +readKeys :: forall k v b i. SQL.Database -> IORef StmtCache -> Domain k v b i -> GasM b i [k] +readKeys _db stmtCache = liftIO . \case DKeySets -> withStmt (_tblReadKeys . _stmtKeyset <$> readIORef stmtCache) $ \stmt -> do parsedKS <- fmap parseAnyKeysetName <$> collect stmt [] case sequence parsedKS of @@ -267,7 +267,7 @@ write' -> GasM b i () write' serial db txId txLog stmtCache wt domain k v = case domain of - DUserTables tbl -> liftIO (checkInsertOk tbl k) >>= \case + DUserTables tbl -> checkInsertOk tbl k >>= \case Nothing -> do encoded <- _encodeRowData serial v liftIO $ do @@ -324,16 +324,15 @@ write' serial db txId txLog stmtCache wt domain k v = SQL.bind stmt [SQL.SQLInteger (fromIntegral i), SQL.SQLText k', SQL.SQLBlob encoded] doWrite stmt (TxLog (renderDomain domain) k' encoded:) where - checkInsertOk :: TableName -> RowKey -> IO (Maybe RowData) checkInsertOk tbl rk = do curr <- read' serial db stmtCache (DUserTables tbl) rk case (curr, wt) of (Nothing, Insert) -> return Nothing - (Just _, Insert) -> throwIO (E.RowFoundException tbl rk) + (Just _, Insert) -> throwM (E.RowFoundException tbl rk) (Nothing, Write) -> return Nothing (Just old, Write) -> return $ Just old (Just old, Update) -> return $ Just old - (Nothing, Update) -> throwIO (E.NoRowFound tbl rk) + (Nothing, Update) -> throwM (E.NoRowFound tbl rk) doWrite stmt txlog = Direct.stepNoCB stmt >>= \case Left _ -> throwIO E.WriteException @@ -343,26 +342,26 @@ write' serial db txId txLog stmtCache wt domain k v = modifyIORef' txLog txlog | otherwise -> throwIO E.MultipleRowsReturnedFromSingleWrite -read' :: forall k v b i. PactSerialise b i -> SQL.Database -> IORef StmtCache -> Domain k v b i -> k -> IO (Maybe v) +read' :: forall k v b i. PactSerialise b i -> SQL.Database -> IORef StmtCache -> Domain k v b i -> k -> GasM b i (Maybe v) read' serial _db stmtCache domain k = case domain of - DKeySets -> withStmt (_tblReadValue . _stmtKeyset <$> readIORef stmtCache) $ + DKeySets -> liftIO $ withStmt (_tblReadValue . _stmtKeyset <$> readIORef stmtCache) $ doRead (renderKeySetName k) (\v -> pure (view document <$> _decodeKeySet serial v)) - DModules -> withStmt (_tblReadValue . _stmtModules <$> readIORef stmtCache) $ + DModules -> liftIO $ withStmt (_tblReadValue . _stmtModules <$> readIORef stmtCache) $ doRead (renderModuleName k) (\v -> pure (view document <$> _decodeModuleData serial v)) DUserTables tbl -> do - tblCache <- _stmtUserTbl <$> readIORef stmtCache + tblCache <- _stmtUserTbl <$> liftIO (readIORef stmtCache) case M.lookup tbl tblCache of - Nothing -> fail "invariant failure: table unknown" - Just stmt -> withStmt (pure $ _tblReadValue stmt) $ doRead (_rowKey k) (\v -> pure (view document <$> _decodeRowData serial v)) + Nothing -> error "invariant failure: table unknown" + Just stmt -> liftIO $ withStmt (pure $ _tblReadValue stmt) $ doRead (_rowKey k) (\v -> pure (view document <$> _decodeRowData serial v)) DDefPacts -> do - withStmt (_tblReadValue . _stmtDefPact <$> readIORef stmtCache) $ + liftIO $ withStmt (_tblReadValue . _stmtDefPact <$> readIORef stmtCache) $ doRead (renderDefPactId k) (\v -> pure (view document <$> _decodeDefPactExec serial v)) DNamespaces -> - withStmt (_tblReadValue . _stmtNamespace <$> readIORef stmtCache) + liftIO $ withStmt (_tblReadValue . _stmtNamespace <$> readIORef stmtCache) (doRead (_namespaceName k) (\v -> pure (view document <$> _decodeNamespace serial v))) where diff --git a/pact/Pact/Core/Persistence/Types.hs b/pact/Pact/Core/Persistence/Types.hs index 1c19e6cd4..12bfb236c 100644 --- a/pact/Pact/Core/Persistence/Types.hs +++ b/pact/Pact/Core/Persistence/Types.hs @@ -214,9 +214,9 @@ newtype GasM b i a data PactDb b i = PactDb { _pdbPurity :: !Purity - , _pdbRead :: forall k v. Domain k v b i -> k -> IO (Maybe v) + , _pdbRead :: forall k v. Domain k v b i -> k -> GasM b i (Maybe v) , _pdbWrite :: forall k v. WriteType -> Domain k v b i -> k -> v -> GasM b i () - , _pdbKeys :: forall k v. Domain k v b i -> IO [k] + , _pdbKeys :: forall k v. Domain k v b i -> GasM b i [k] , _pdbCreateUserTable :: TableName -> GasM b i () , _pdbBeginTx :: ExecutionMode -> IO (Maybe TxId) , _pdbCommitTx :: IO [TxLog ByteString] @@ -237,12 +237,7 @@ makeClassy ''PactDb data GuardTableOp = GtRead | GtSelect - | GtWithRead - | GtWithDefaultRead | GtKeys - | GtTxIds - | GtTxLog - | GtKeyLog | GtWrite | GtCreateTable deriving Show diff --git a/pact/Pact/Core/Persistence/Utils.hs b/pact/Pact/Core/Persistence/Utils.hs index ead5f6590..a8e55ec0e 100644 --- a/pact/Pact/Core/Persistence/Utils.hs +++ b/pact/Pact/Core/Persistence/Utils.hs @@ -7,12 +7,19 @@ module Pact.Core.Persistence.Utils , liftGasM , ignoreGas , chargeGasM + , lookupModule + , lookupModuleData + , getModuleData + , getModule + , getModuleMember + , getModuleMemberWithHash ) where import Control.Lens import Control.Exception.Safe import Control.Monad.Reader import Data.IORef +import qualified Data.Map.Strict as M import Pact.Core.Environment import Pact.Core.Errors @@ -20,6 +27,9 @@ import Pact.Core.Names import Pact.Core.Persistence.Types import Pact.Core.Gas import Control.Monad.Except +import Pact.Core.IR.Term +import Pact.Core.Hash +import Data.Maybe (mapMaybe) evalWrite :: i -> PactDb b i -> WriteType -> Domain k v b i -> k -> v -> EvalM e b i () @@ -36,6 +46,77 @@ chargeGasM gasArgs = do (gasEnv, info, stack) <- ask either throwError return =<< liftIO (chargeGasArgsM gasEnv info stack gasArgs) +-- | lookupModuleData for only modules +lookupModule :: i -> ModuleName -> EvalM e b i (Maybe (EvalModule b i)) +lookupModule info mn = do + pdb <- viewEvalEnv eePactDb + use (esLoaded . loModules . at mn) >>= \case + Just (ModuleData md _) -> pure (Just md) + Just (InterfaceData _ _) -> + throwExecutionError info (ExpectedModule mn) + Nothing -> do + liftGasM info (_pdbRead pdb DModules mn) >>= \case + Just mdata@(ModuleData md deps) -> do + let newLoaded = M.fromList $ toFqDep mn (_mHash md) <$> _mDefs md + (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps + (esLoaded . loModules) %= M.insert mn mdata + pure (Just md) + Just (InterfaceData _ _) -> + throwExecutionError info (ExpectedModule mn) + Nothing -> pure Nothing + +-- | lookupModuleData modules and interfaces +lookupModuleData :: i -> ModuleName -> EvalM e b i (Maybe (ModuleData b i)) +lookupModuleData info mn = do + pdb <- viewEvalEnv eePactDb + use (esLoaded . loModules . at mn) >>= \case + Just md -> pure (Just md) + Nothing -> do + liftGasM info (_pdbRead pdb DModules mn) >>= \case + Just mdata@(ModuleData md deps) -> do + let newLoaded = M.fromList $ toFqDep mn (_mHash md) <$> _mDefs md + (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps + (esLoaded . loModules) %= M.insert mn mdata + pure (Just mdata) + Just mdata@(InterfaceData iface deps) -> do + let ifDefs = mapMaybe ifDefToDef (_ifDefns iface) + let newLoaded = M.fromList $ toFqDep mn (_ifHash iface) <$> ifDefs + (esLoaded . loAllLoaded) %= M.union newLoaded . M.union deps + (esLoaded . loModules) %= M.insert mn mdata + pure (Just mdata) + Nothing -> pure Nothing + +-- | getModuleData, but only for modules, no interfaces +getModule :: i -> ModuleName -> EvalM e b i (EvalModule b i) +getModule info mn = lookupModule info mn >>= \case + Just md -> pure md + Nothing -> throwExecutionError info (ModuleDoesNotExist mn) + +-- | Get or load a module or interface based on the module name +getModuleData :: i -> ModuleName -> EvalM e b i (ModuleData b i) +getModuleData info mn = lookupModuleData info mn >>= \case + Just md -> pure md + Nothing -> throwExecutionError info (ModuleDoesNotExist mn) + +-- | Returns a module member, but only for modules, no interfaces +getModuleMember :: i -> QualifiedName -> EvalM e b i (EvalDef b i) +getModuleMember info (QualifiedName qn mn) = do + md <- getModule info mn + case findDefInModule qn md of + Just d -> pure d + Nothing -> do + let fqn = FullyQualifiedName mn qn (_mHash md) + throwExecutionError info (ModuleMemberDoesNotExist fqn) + +getModuleMemberWithHash :: i -> QualifiedName -> EvalM e b i (EvalDef b i, ModuleHash) +getModuleMemberWithHash info (QualifiedName qn mn) = do + md <- getModule info mn + case findDefInModule qn md of + Just d -> pure (d, _mHash md) + Nothing -> do + let fqn = FullyQualifiedName mn qn (_mHash md) + throwExecutionError info (ModuleMemberDoesNotExist fqn) + -- | A utility function that lifts a `GasM` action into a `MonadEval` action. liftGasM :: i -> GasM b i a -> EvalM e b i a liftGasM info action = do diff --git a/pact/Pact/Core/Repl/Compile.hs b/pact/Pact/Core/Repl/Compile.hs index 15cb407d2..923c51576 100644 --- a/pact/Pact/Core/Repl/Compile.hs +++ b/pact/Pact/Core/Repl/Compile.hs @@ -129,7 +129,11 @@ checkReplNativesEnabled = \case interpretEvalBigStep :: ReplInterpreter interpretEvalBigStep = - Interpreter { eval = evalBigStep, resumePact = evalResumePact, interpretGuard = interpretGuardBigStep} + Interpreter + { eval = evalBigStep + , resumePact = evalResumePact + , interpretGuard = interpretGuardBigStep + , evalWithCapability = evalWithCap} where evalBigStep purity term = CEK.eval purity replBuiltinEnv term @@ -137,10 +141,15 @@ interpretEvalBigStep = CEK.evalResumePact info replBuiltinEnv pactExec interpretGuardBigStep info g = CEK.interpretGuard info replBuiltinEnv g + evalWithCap info purity ct term = + CEK.evalWithinCap info purity replBuiltinEnv ct term interpretEvalDirect :: ReplInterpreter interpretEvalDirect = - Interpreter { eval = evalDirect, resumePact = evalResumePact, interpretGuard = interpretGuardDirect} + Interpreter { eval = evalDirect + , resumePact = evalResumePact + , interpretGuard = interpretGuardDirect + , evalWithCapability = evalWithCap} where evalDirect purity term = Direct.eval purity Direct.replBuiltinEnv term @@ -148,6 +157,8 @@ interpretEvalDirect = Direct.evalResumePact info Direct.replBuiltinEnv pactExec interpretGuardDirect info g = Direct.interpretGuard info Direct.replBuiltinEnv g + evalWithCap info purity ct term = + Direct.evalWithinCap info purity Direct.replBuiltinEnv ct term isPactFile :: FilePath -> Bool isPactFile f = takeExtension f == ".pact" diff --git a/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs b/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs index c16259c22..13e2014b2 100644 --- a/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs +++ b/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs @@ -411,10 +411,9 @@ envExecConfig info b cont handler _env = \case envNamespacePolicy :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo envNamespacePolicy info b cont handler _env = \case [VBool allowRoot, VClosure (C clo)] -> do - pdb <- viewEvalEnv eePactDb let qn = fqnToQualName (_cloFqName clo) when (_cloArity clo /= 2) $ throwNativeExecutionError info b "Namespace manager function has invalid argument length" - getModuleMember info pdb qn >>= \case + getModuleMember info qn >>= \case Dfun _ -> do let nsp = SmartNamespacePolicy allowRoot qn replEvalEnv . eeNamespacePolicy .== nsp diff --git a/pact/Pact/Core/Serialise.hs b/pact/Pact/Core/Serialise.hs index 46f560dcc..33889805a 100644 --- a/pact/Pact/Core/Serialise.hs +++ b/pact/Pact/Core/Serialise.hs @@ -41,6 +41,7 @@ import Codec.CBOR.Read (deserialiseFromBytes) import qualified Pact.Core.Serialise.LegacyPact as LegacyPact import qualified Pact.Core.Serialise.CBOR_V1 as V1 +import qualified Pact.Core.Legacy.LegacyPactValue as LegacyPact import Pact.Core.Info (SpanInfo) import Data.Default @@ -119,7 +120,7 @@ serialisePact = PactSerialise , _encodeRowData = gEncodeRowData , _decodeRowData = \bs -> - LegacyDocument <$> LegacyPact.decodeRowData bs + LegacyDocument <$> LegacyPact.decodeLegacy bs <|> docDecode bs (\case V1_CBOR -> V1.decodeRowData ) diff --git a/pact/Pact/Core/Serialise/LegacyPact/Types.hs b/pact/Pact/Core/Serialise/LegacyPact/Types.hs index a3741e55b..53a7d953e 100644 --- a/pact/Pact/Core/Serialise/LegacyPact/Types.hs +++ b/pact/Pact/Core/Serialise/LegacyPact/Types.hs @@ -7,6 +7,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE InstanceSigs #-} module Pact.Core.Serialise.LegacyPact.Types where diff --git a/profile-tx/ProfileTx.hs b/profile-tx/ProfileTx.hs index a468f2334..53a79adf2 100644 --- a/profile-tx/ProfileTx.hs +++ b/profile-tx/ProfileTx.hs @@ -40,9 +40,6 @@ import Pact.Core.SPV import qualified Pact.Core.Syntax.Parser as Lisp import qualified Pact.Core.Syntax.Lexer as Lisp import qualified Pact.Core.Syntax.LexUtils as Lisp -import qualified Pact.Core.IR.Eval.CEK as CEK -import qualified Pact.Core.IR.Eval.CoreBuiltin as CEK -import qualified Pact.Core.IR.Eval.Direct.Evaluator as Direct import Pact.Core.Gas.TableGasModel import Pact.Core.Gas import Pact.Core.Namespace @@ -70,25 +67,6 @@ contractsPath = "gasmodel" "contracts" mkKs :: PublicKeyText -> PactValue mkKs a = PGuard $ GKeyset $ KeySet (S.singleton a) KeysAll -interpretBigStep :: Interpreter ExecRuntime CoreBuiltin SpanInfo -interpretBigStep = - Interpreter runGuard runTerm evalResumePact - where - runTerm purity term = CEK.eval purity eEnv term - runGuard info g = CEK.interpretGuard info eEnv g - eEnv = CEK.coreBuiltinEnv @ExecRuntime - evalResumePact info pactExec = CEK.evalResumePact info eEnv pactExec - - -interpretDirect :: Interpreter ExecRuntime CoreBuiltin SpanInfo -interpretDirect = - Interpreter runGuard runTerm evalResumePact - where - runTerm purity term = Direct.eval purity eEnv term - runGuard info g = Direct.interpretGuard info eEnv g - eEnv = Direct.coreBuiltinEnv - evalResumePact info pactExec = Direct.evalResumePact info eEnv pactExec - data CoinBenchSenders = CoinBenchSenderA @@ -265,7 +243,7 @@ _testCoinTransfer = withSqlitePactDb serialisePact_raw_spaninfo (T.pack benchmar t <- liftEither $ parseOnlyExpr termText _dsOut <$> runDesugarTerm t term <- getRightIO eterm - (out, _) <- runEvalM (ExecEnv ee) es (eval interpretDirect PImpure term) + (out, _) <- runEvalM (ExecEnv ee) es (eval evalDirectInterpreter PImpure term) print out unsafeModuleHash :: Text -> Hash @@ -284,11 +262,11 @@ withTx pdb act = do runCoinXferDirect :: PactDb CoreBuiltin SpanInfo -> IO () runCoinXferDirect pdb = do ee <- setupBenchEvalEnv pdb (transferSigners CoinBenchSenderA CoinBenchSenderB) (PObject mempty) - (m, es) <- runEvalM (ExecEnv ee) def $ getModule def pdb (ModuleName "coin" Nothing) + (m, es) <- runEvalM (ExecEnv ee) def $ getModule def (ModuleName "coin" Nothing) _ <- getRightIO m let es' = def {_esLoaded=_esLoaded es} forM_ [1 :: Integer .. 1000] $ \_ -> withTx pdb $ do - (out, _) <- runEvalM (ExecEnv ee) es' $ eval interpretBigStep PImpure term + (out, _) <- runEvalM (ExecEnv ee) es' $ eval evalInterpreter PImpure term writeIORef (_geGasRef $ _eeGasEnv ee) mempty either throw print out pure () diff --git a/test-utils/Pact/Core/Gen.hs b/test-utils/Pact/Core/Gen.hs index 59aa4cc1d..91b7ad7e0 100644 --- a/test-utils/Pact/Core/Gen.hs +++ b/test-utils/Pact/Core/Gen.hs @@ -19,6 +19,9 @@ import qualified Data.Vector as Vec import Hedgehog hiding (Var) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range + +import Pact.Time +import Pact.Time.Internal(UTCTime(..)) import Pact.Core.Names import Pact.Core.Guards import Pact.Core.Hash (Hash(..), ModuleHash(..)) @@ -34,6 +37,7 @@ import Pact.Core.PactValue import Pact.Core.DefPacts.Types import Pact.Core.ChainData (ChainId(..)) import Pact.Core.Namespace (Namespace(..)) +import Data.Coerce namespaceNameGen :: Gen NamespaceName namespaceNameGen = NamespaceName <$> identGen @@ -160,17 +164,17 @@ schemaGen = do typeGen :: Gen Type typeGen = Gen.recursive Gen.choice - [ TyPrim <$> tyPrimGen - , TyModRef <$> Gen.set (Range.linear 0 10) moduleNameGen - , pure TyAny - , pure TyCapToken - , pure TyAnyList - , pure TyAnyObject - ] - [ TyList <$> typeGen - , TyObject <$> schemaGen - , TyTable <$> schemaGen - ] + [ TyPrim <$> tyPrimGen + , TyModRef <$> Gen.set (Range.linear 0 10) moduleNameGen + , pure TyAny + , pure TyCapToken + , pure TyAnyList + , pure TyAnyObject + ] + [ TyList <$> typeGen + , TyObject <$> schemaGen + , TyTable <$> schemaGen + ] argGen :: Gen i -> Gen (Arg Type i) argGen i = do @@ -401,13 +405,20 @@ guardGen n = Gen.choice [gKeySetGen, gKeySetRefGen] gKeySetRefGen = GKeySetRef <$> keySetNameGen -- gUserGuardGen = GUserGuard <$> userGuardGen (depth - 1) +timeGen :: Gen UTCTime +timeGen = + coerce <$> Gen.int64 Range.constantBounded + pactValueGen :: Gen PactValue pactValueGen = Gen.recursive Gen.choice [ PLiteral <$> literalGen , PGuard <$> guardGen qualifiedNameGen + , PTime <$> timeGen ] - [ PList . Vec.fromList <$> Gen.list (Range.linear 0 5) pactValueGen - , PObject <$> (Gen.map (Range.linear 0 5) ((,) <$> fieldGen <*> pactValueGen)) + [ PList . Vec.fromList <$> Gen.list (Range.linear 1 5) pactValueGen + , PObject <$> (Gen.map (Range.linear 1 5) ((,) <$> fieldGen <*> pactValueGen)) + , PCapToken <$> + (CapToken <$> fullyQualifiedNameGen <*> (Gen.list (Range.linear 0 10) pactValueGen)) ] chainIdGen :: Gen ChainId diff --git a/test-utils/Pact/Core/PactDbRegression.hs b/test-utils/Pact/Core/PactDbRegression.hs index 08e67a4c6..12fc43823 100644 --- a/test-utils/Pact/Core/PactDbRegression.hs +++ b/test-utils/Pact/Core/PactDbRegression.hs @@ -45,7 +45,7 @@ runPactDbRegression pdb = do rowEnc <- ignoreGas def $ _encodeRowData serialisePact_raw_spaninfo row ignoreGas def $ _pdbWrite pdb Insert (DUserTables usert) (RowKey "key1") row row' <- do - _pdbRead pdb (DUserTables usert) (RowKey "key1") >>= \case + ignoreGas def (_pdbRead pdb (DUserTables usert) (RowKey "key1")) >>= \case Nothing -> error "expected row" Just r -> pure r assertEqual "row should be identical to its saved/recalled value" row row' @@ -58,7 +58,7 @@ runPactDbRegression pdb = do row2Enc <- ignoreGas def $ _encodeRowData serialisePact_raw_spaninfo row2 ignoreGas def $ _pdbWrite pdb Update (DUserTables usert) (RowKey "key1") row2 - row2' <- _pdbRead pdb (DUserTables usert) (RowKey "key1") >>= \case + row2' <- ignoreGas def $ _pdbRead pdb (DUserTables usert) (RowKey "key1") >>= \case Nothing -> error "expected row" Just r -> pure r assertEqual "user update should overwrite with new value" row2 row2' @@ -67,7 +67,7 @@ runPactDbRegression pdb = do ks = KeySet (S.fromList [PublicKeyText "skdjhfskj"]) KeysAll ksEnc = _encodeKeySet serialisePact_raw_spaninfo ks _ <- ignoreGas def $ _pdbWrite pdb Write DKeySets (KeySetName "ks1" Nothing) ks - ks' <- _pdbRead pdb DKeySets (KeySetName "ks1" Nothing) >>= \case + ks' <- ignoreGas def $ _pdbRead pdb DKeySets (KeySetName "ks1" Nothing) >>= \case Nothing -> error "expected keyset" Just r -> pure r assertEqual "keyset should be equal after storage/retrieval" ks ks' @@ -79,7 +79,7 @@ runPactDbRegression pdb = do let mdEnc = _encodeModuleData serialisePact_raw_spaninfo md ignoreGas def $ _pdbWrite pdb Write DModules mn md - md' <- _pdbRead pdb DModules mn >>= \case + md' <- ignoreGas def $ _pdbRead pdb DModules mn >>= \case Nothing -> error "Expected module" Just r -> pure r assertEqual "module should be identical to its saved/recalled value" md md' @@ -96,21 +96,21 @@ runPactDbRegression pdb = do _ <- _pdbBeginTx pdb Transactional ignoreGas def $ _pdbWrite pdb Insert (DUserTables usert) (RowKey "key2") row - r1 <- _pdbRead pdb (DUserTables usert) (RowKey "key2") >>= \case + r1 <- ignoreGas def $ _pdbRead pdb (DUserTables usert) (RowKey "key2") >>= \case Nothing -> error "expected row" Just r -> pure r assertEqual "user insert key2 pre-rollback" row r1 do - rkeys <- _pdbKeys pdb (DUserTables usert) + rkeys <- ignoreGas def $ _pdbKeys pdb (DUserTables usert) assertEqual "keys pre-rollback [key1, key2]" [RowKey "key1", RowKey "key2"] rkeys _pdbRollbackTx pdb _ <- _pdbBeginTx pdb Transactional - r2 <- _pdbRead pdb (DUserTables usert) (RowKey "key2") + r2 <- ignoreGas def $ _pdbRead pdb (DUserTables usert) (RowKey "key2") assertEqual "rollback erases key2" Nothing r2 - rkeys2 <- _pdbKeys pdb (DUserTables usert) + rkeys2 <- ignoreGas def $ _pdbKeys pdb (DUserTables usert) assertEqual "keys post-rollback [key1]" [RowKey "key1"] rkeys2 _ <- _pdbCommitTx pdb @@ -125,5 +125,5 @@ loadModule = do Right _ <- runEvalMResult (ExecEnv ee) def $ do p <- liftEither (parseOnlyProgram src) traverse (interpretTopLevel evalInterpreter) p - Just md <- _pdbRead pdb DModules (ModuleName "test" Nothing) + Just md <- ignoreGas def $ _pdbRead pdb DModules (ModuleName "test" Nothing) pure md