diff --git a/docs/en/pact-functions.md b/docs/en/pact-functions.md index 0658420c9..a5e9b1a11 100644 --- a/docs/en/pact-functions.md +++ b/docs/en/pact-functions.md @@ -622,16 +622,6 @@ Create table TABLE. Top level only: this function will fail if used in module code. -### describe-keyset {#describe-keyset} - -*keyset* `string` *→* `object:*` - - -Get metadata for KEYSET. - -Top level only: this function will fail if used in module code. - - ### describe-module {#describe-module} *module* `string` *→* `object:*` @@ -1356,598 +1346,25 @@ Define keyset as NAME with KEYSET, or if unspecified, read NAME from message pay Top level only: this function will fail if used in module code. -### enforce-keyset {#enforce-keyset} - -*guard* `guard` *→* `bool` - -*keysetname* `string` *→* `bool` - - -Execute GUARD, or defined keyset KEYSETNAME, to enforce desired predicate logic. -```lisp -(enforce-keyset 'admin-keyset) -(enforce-keyset row-guard) -``` - - -### keys-2 {#keys-2} - -*count* `integer` *matched* `integer` *→* `bool` - - -Keyset predicate function to match at least 2 keys in keyset. -```lisp -pact> (keys-2 3 1) -false -``` - - -### keys-all {#keys-all} - -*count* `integer` *matched* `integer` *→* `bool` - - -Keyset predicate function to match all keys in keyset. -```lisp -pact> (keys-all 3 3) -true -``` - - -### keys-any {#keys-any} - -*count* `integer` *matched* `integer` *→* `bool` - - -Keyset predicate function to match any (at least 1) key in keyset. -```lisp -pact> (keys-any 10 1) -true -``` - - -### read-keyset {#read-keyset} - -*key* `string` *→* `keyset` - - -Read KEY from message data body as keyset ({ "keys": KEYLIST, "pred": PREDFUN }). PREDFUN should resolve to a keys predicate. -```lisp -(read-keyset "admin-keyset") -``` - -## Capabilities {#Capabilities} - -### compose-capability {#compose-capability} - -*capability* ` -> bool` *→* `bool` - - -Specifies and requests grant of CAPABILITY which is an application of a 'defcap' production, only valid within a (distinct) 'defcap' body, as a way to compose CAPABILITY with the outer capability such that the scope of the containing 'with-capability' call will "import" this capability. Thus, a call to '(with-capability (OUTER-CAP) OUTER-BODY)', where the OUTER-CAP defcap calls '(compose-capability (INNER-CAP))', will result in INNER-CAP being granted in the scope of OUTER-BODY. -```lisp -(compose-capability (TRANSFER src dest)) -``` - - -### create-module-guard {#create-module-guard} - -*name* `string` *→* `guard` - - -Defines a guard by NAME that enforces the current module admin predicate. - - -### create-pact-guard {#create-pact-guard} - -*name* `string` *→* `guard` - - -Defines a guard predicate by NAME that captures the results of 'pact-id'. At enforcement time, the success condition is that at that time 'pact-id' must return the same value. In effect this ensures that the guard will only succeed within the multi-transaction identified by the pact id. - - -### create-user-guard {#create-user-guard} - -*closure* ` -> bool` *→* `guard` - - -Defines a custom guard CLOSURE whose arguments are strictly evaluated at definition time, to be supplied to indicated function at enforcement time. - - -### enforce-guard {#enforce-guard} - -*guard* `guard` *→* `bool` - -*keysetname* `string` *→* `bool` - - -Execute GUARD, or defined keyset KEYSETNAME, to enforce desired predicate logic. -```lisp -(enforce-guard 'admin-keyset) -(enforce-guard row-guard) -``` - - -### install-capability {#install-capability} - -*capability* ` -> bool` *→* `string` - - -Specifies, and provisions install of, a _managed_ CAPABILITY, defined in a 'defcap' in which a '@managed' tag designates a single parameter to be managed by a specified function. After install, CAPABILITY must still be brought into scope using 'with-capability', at which time the 'manager function' is invoked to validate the request. The manager function is of type 'managed:

requested:

->

', where '

' indicates the type of the managed parameter, such that for '(defcap FOO (bar:string baz:integer) @managed baz FOO-mgr ...)', the manager function would be '(defun FOO-mgr:integer (managed:integer requested:integer) ...)'. Any capability matching the 'static' (non-managed) parameters will cause this function to be invoked with the current managed value and that of the requested capability. The function should perform whatever logic, presumably linear, to validate the request, and return the new managed value representing the 'balance' of the request. NOTE that signatures scoped to a managed capability cause the capability to be automatically provisioned for install similarly to one installed with this function. -```lisp -(install-capability (PAY "alice" "bob" 10.0)) -``` - - -### keyset-ref-guard {#keyset-ref-guard} - -*keyset-ref* `string` *→* `guard` - - -Creates a guard for the keyset registered as KEYSET-REF with 'define-keyset'. Concrete keysets are themselves guard types; this function is specifically to store references alongside other guards in the database, etc. - - -### require-capability {#require-capability} - -*capability* ` -> bool` *→* `bool` - - -Specifies and tests for existing grant of CAPABILITY, failing if not found in environment. -```lisp -(require-capability (TRANSFER src dest)) -``` - - -### with-capability {#with-capability} - -*capability* ` -> bool` *body* `[*]` *→* `` - - -Specifies and requests grant of _acquired_ CAPABILITY which is an application of a 'defcap' production. Given the unique token specified by this application, ensure that the token is granted in the environment during execution of BODY. 'with-capability' can only be called in the same module that declares the corresponding 'defcap', otherwise module-admin rights are required. If token is not present, the CAPABILITY is evaluated, with successful completion resulting in the installation/granting of the token, which will then be revoked upon completion of BODY. Nested 'with-capability' calls for the same token will detect the presence of the token, and will not re-apply CAPABILITY, but simply execute BODY. 'with-capability' cannot be called from within an evaluating defcap. Acquire of a managed capability results in emission of the equivalent event. -```lisp -(with-capability (UPDATE-USERS id) (update users id { salary: new-salary })) -``` - -## SPV {#SPV} - -### verify-spv {#verify-spv} - -*type* `string` *payload* `object:` *→* `object:` - - -Performs a platform-specific spv proof of type TYPE on PAYLOAD. The format of the PAYLOAD object depends on TYPE, as does the format of the return object. Platforms such as Chainweb will document the specific payload types and return values. -```lisp -(verify-spv "TXOUT" (read-msg "proof")) -``` - -## Commitments {#Commitments} - -### decrypt-cc20p1305 {#decrypt-cc20p1305} - -*ciphertext* `string` *nonce* `string` *aad* `string` *mac* `string` *public-key* `string` *secret-key* `string` *→* `string` - - -Perform decryption of CIPHERTEXT using the CHACHA20-POLY1305 Authenticated Encryption with Associated Data (AEAD) construction described in IETF RFC 7539. CIPHERTEXT is an unpadded base64url string. NONCE is a 12-byte base64 string. AAD is base64 additional authentication data of any length. MAC is the "detached" base64 tag value for validating POLY1305 authentication. PUBLIC-KEY and SECRET-KEY are base-16 Curve25519 values to form the DH symmetric key.Result is unpadded base64URL. -```lisp -(decrypt-cc20p1305 ciphertext nonce aad mac pubkey privkey) -``` - - -### validate-keypair {#validate-keypair} - -*public* `string` *secret* `string` *→* `bool` - - -Enforce that the Curve25519 keypair of (PUBLIC,SECRET) match. Key values are base-16 strings of length 32. -```lisp -(validate-keypair pubkey privkey) -``` - -## REPL-only functions {#repl-lib} - -The following functions are loaded automatically into the interactive REPL, or within script files with a `.repl` extension. They are not available for blockchain-based execution. - - -### begin-tx {#begin-tx} - - *→* `string` - -*name* `string` *→* `string` - - -Begin transaction with optional NAME. -```lisp -pact> (begin-tx "load module") -"Begin Tx 0: load module" -``` - - -### bench {#bench} - -*exprs* `*` *→* `string` - - -Benchmark execution of EXPRS. -```lisp -(bench (+ 1 2)) -``` - - -### commit-tx {#commit-tx} - - *→* `string` - - -Commit transaction. -```lisp -pact> (begin-tx) (commit-tx) -"Commit Tx 0" -``` - - -### continue-pact {#continue-pact} - -*step* `integer` *→* `string` - -*step* `integer` *rollback* `bool` *→* `string` - -*step* `integer` *rollback* `bool` *pact-id* `string` *→* `string` - -*step* `integer` *rollback* `bool` *pact-id* `string` *yielded* `object:<{y}>` *→* `string` - - -Continue previously-initiated pact identified STEP, optionally specifying ROLLBACK (default is false), PACT-ID of the pact to be continued (defaults to the pact initiated in the current transaction, if one is present), and YIELDED value to be read with 'resume' (if not specified, uses yield in most recent pact exec, if any). -```lisp -(continue-pact 1) -(continue-pact 1 true) -(continue-pact 1 false "[pact-id-hash]")) -(continue-pact 2 1 false "[pact-id-hash]" { "rate": 0.9 }) -``` - - -### env-chain-data {#env-chain-data} - -*new-data* `object:~{public-chain-data}` *→* `string` - - -Update existing entries of 'chain-data' with NEW-DATA, replacing those items only. -```lisp -pact> (env-chain-data { "chain-id": "TestNet00/2", "block-height": 20 }) -"Updated public metadata" -``` - - -### env-data {#env-data} - -*json* `],object:<{o}>,keyset]>` *→* `string` - - -Set transaction JSON data, either as encoded string, or as pact types coerced to JSON. -```lisp -pact> (env-data { "keyset": { "keys": ["my-key" "admin-key"], "pred": "keys-any" } }) -"Setting transaction data" -``` - - -### env-enable-repl-natives {#env-enable-repl-natives} - -*enable* `bool` *→* `string` - - -Control whether REPL native functions are allowed in module code. When enabled, fixture functions like 'env-sigs' are allowed in module code. -```lisp -pact> (env-enable-repl-natives true) -"Repl natives enabled" -``` - - -### env-entity {#env-entity} - - *→* `string` - -*entity* `string` *→* `string` - - -Set environment confidential ENTITY id, or unset with no argument. -```lisp -(env-entity "my-org") -(env-entity) -``` - - -### env-events {#env-events} - -*clear* `bool` *→* `[object:*]` - - -Retreive any accumulated events and optionally clear event state. Object returned has fields 'name' (fully-qualified event name), 'params' (event parameters), 'module-hash' (hash of emitting module). -```lisp -(env-events true) -``` - - -### env-exec-config {#env-exec-config} - -*flags* `[string]` *→* `[string]` - - *→* `[string]` - - -Queries, or with arguments, sets execution config flags. Valid flags: ["AllowReadInLocal","DisableHistoryInTransactionalMode","DisableModuleInstall","DisablePactEvents","OldReadOnlyBehavior","PreserveModuleIfacesBug","PreserveModuleNameBug","PreserveNsModuleInstallBug","PreserveShowDefs"] -```lisp -pact> (env-exec-config ['DisableHistoryInTransactionalMode]) (env-exec-config) -["DisableHistoryInTransactionalMode"] -``` - - -### env-gas {#env-gas} - - *→* `integer` - -*gas* `integer` *→* `string` - - -Query gas state, or set it to GAS. Note that certain plaforms may charge additional gas that is not captured by the interpreter gas model, such as an overall transaction-size cost. -```lisp -pact> (env-gasmodel "table") (env-gaslimit 10) (env-gas 0) (map (+ 1) [1 2 3]) (env-gas) -7 -``` - - -### env-gaslimit {#env-gaslimit} - -*limit* `integer` *→* `string` - - -Set environment gas limit to LIMIT. - - -### env-gaslog {#env-gaslog} - - *→* `string` - - -Enable and obtain gas logging. Bracket around the code whose gas logs you want to inspect. -```lisp -pact> (env-gasmodel "table") (env-gaslimit 10) (env-gaslog) (map (+ 1) [1 2 3]) (env-gaslog) -["TOTAL: 7" "map:GUnreduced: 4" "+:GUnreduced: 1" "+:GUnreduced: 1" "+:GUnreduced: 1"] -``` - - -### env-gasmodel {#env-gasmodel} - -*model* `string` *→* `string` - - *→* `string` - -*model* `string` *rate* `integer` *→* `string` - - -Update or query current gas model. With just MODEL, "table" is supported; with MODEL and RATE, 'fixed' is supported. With no args, output current model. -```lisp -pact> (env-gasmodel) -"Current gas model is 'fixed 0': constant rate gas model with fixed rate 0" -pact> (env-gasmodel 'table) -"Set gas model to table-based cost model" -pact> (env-gasmodel 'fixed 1) -"Set gas model to constant rate gas model with fixed rate 1" -``` - - -### env-gasprice {#env-gasprice} - -*price* `decimal` *→* `string` - - -Set environment gas price to PRICE. - - -### env-gasrate {#env-gasrate} - -*rate* `integer` *→* `string` - - -Update gas model to charge constant RATE. - - -### env-hash {#env-hash} - -*hash* `string` *→* `string` - - -Set current transaction hash. HASH must be an unpadded base64-url encoded BLAKE2b 256-bit hash. -```lisp -pact> (env-hash (hash "hello")) -"Set tx hash to Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8" -``` - - -### env-keys {#env-keys} - -*keys* `[string]` *→* `string` - - -DEPRECATED in favor of 'set-sigs'. Set transaction signer KEYS. See 'env-sigs' for setting keys with associated capabilities. -```lisp -pact> (env-keys ["my-key" "admin-key"]) -"Setting transaction keys" -``` - - -### env-namespace-policy {#env-namespace-policy} - -*allow-root* `bool` *ns-policy-fun* `ns:string ns-admin:guard -> bool` *→* `string` - - -Install a managed namespace policy specifying ALLOW-ROOT and NS-POLICY-FUN. -```lisp -(env-namespace-policy (my-ns-policy-fun)) -``` - - -### env-sigs {#env-sigs} - -*sigs* `[object:*]` *→* `string` - +### describe-guard {#describe-guard} -Set transaction signature keys and capabilities. SIGS is a list of objects with "key" specifying the signer key, and "caps" specifying a list of associated capabilities. -```lisp -(env-sigs [{'key: "my-key", 'caps: [(accounts.USER_GUARD "my-account")]}, {'key: "admin-key", 'caps: []} -``` - - -### expect {#expect} - -*doc* `string` *expected* `` *actual* `` *→* `string` - - -Evaluate ACTUAL and verify that it equals EXPECTED. -```lisp -pact> (expect "Sanity prevails." 4 (+ 2 2)) -"Expect: success: Sanity prevails." -``` - - -### expect-failure {#expect-failure} - -*doc* `string` *exp* `` *→* `string` - -*doc* `string` *err* `string` *exp* `` *→* `string` - - -Evaluate EXP and succeed only if it throws an error. -```lisp -pact> (expect-failure "Enforce fails on false" (enforce false "Expected error")) -"Expect failure: success: Enforce fails on false" -pact> (expect-failure "Enforce fails with message" "Expected error" (enforce false "Expected error")) -"Expect failure: success: Enforce fails with message" -``` - - -### expect-that {#expect-that} - -*doc* `string` *pred* `value: -> bool` *exp* `` *→* `string` - - -Evaluate EXP and succeed if value passes predicate PRED. -```lisp -pact> (expect-that "addition" (< 2) (+ 1 2)) -"Expect-that: success: addition" -pact> (expect-that "addition" (> 2) (+ 1 2)) -"FAILURE: addition: did not satisfy (> 2) : 3:integer" -``` - - -### format-address {#format-address} - -*scheme* `string` *public-key* `string` *→* `string` - - -Transform PUBLIC-KEY into an address (i.e. a Pact Runtime Public Key) depending on its SCHEME. +*guard* `string` *→* `object:*` -### load {#load} - -*file* `string` *→* `string` - -*file* `string` *reset* `bool` *→* `string` - - -Load and evaluate FILE, resetting repl state beforehand if optional RESET is true. +Get metadata for GUARD. ```lisp -(load "accounts.repl") +pact> (env-data {'k: ['abc]}) (define-keyset 'ks (read-keyset 'k)) (describe-guard (keyset-ref-guard 'ks)) +{"keyset": KeySet {keys: [abc],pred: keys-all},"name": "ks","type": "KeySetRef"} ``` - -### mock-spv {#mock-spv} - -*type* `string` *payload* `object:*` *output* `object:*` *→* `string` - - -Mock a successful call to 'spv-verify' with TYPE and PAYLOAD to return OUTPUT. -```lisp -(mock-spv "TXOUT" { 'proof: "a54f54de54c54d89e7f" } { 'amount: 10.0, 'account: "Dave", 'chainId: "1" }) -``` - - -### pact-state {#pact-state} - - *→* `object:*` - -*clear* `bool` *→* `object:*` - - -Inspect state from most recent pact execution. Returns object with fields 'pactId': pact ID; 'yield': yield result or 'false' if none; 'step': executed step; 'executed': indicates if step was skipped because entity did not match. With CLEAR argument, erases pact from repl state. -```lisp -(pact-state) -(pact-state true) -``` - - -### print {#print} - -*value* `` *→* `string` - - -Output VALUE to terminal as unquoted, unescaped text. - - -### rollback-tx {#rollback-tx} - - *→* `string` - - -Rollback transaction. -```lisp -pact> (begin-tx "Third Act") (rollback-tx) -"Rollback Tx 0: Third Act" -``` - - -### sig-keyset {#sig-keyset} - - *→* `keyset` - - -Convenience function to build a keyset from keys present in message signatures, using 'keys-all' as the predicate. - - -### test-capability {#test-capability} - -*capability* ` -> bool` *→* `string` - - -Acquire (if unmanaged) or install (if managed) CAPABILITY. CAPABILITY and any composed capabilities are in scope for the rest of the transaction. -```lisp -(test-capability (MY-CAP)) -``` - - -### typecheck {#typecheck} - -*module* `string` *→* `string` - -*module* `string` *debug* `bool` *→* `string` - - -Typecheck MODULE, optionally enabling DEBUG output. - - -### verify {#verify} - -*module* `string` *→* `string` - - -Verify MODULE, checking that all properties hold. +Top level only: this function will fail if used in module code. -### with-applied-env {#with-applied-env} +### describe-keyset {#describe-keyset} -*exec* `` *→* `` +*keyset* `string` *→* `object:*` -Evaluate EXEC with any pending environment changes applied. Normally, environment changes must execute at top-level for the change to take effect. This allows scoped application of non-toplevel environment changes. +Get metadata for KEYSET. ```lisp -pact> (let ((a 1)) (env-data { 'b: 1 }) (with-applied-env (+ a (read-integer 'b)))) -2 -``` - +pact> (env-data {"k": { "pred": "keys-all", "keys": ["abc"] }) (describe-keyset (read-keyset 'k)) diff --git a/src/Pact/Native/Db.hs b/src/Pact/Native/Db.hs index 528139a36..e9de5bc82 100644 --- a/src/Pact/Native/Db.hs +++ b/src/Pact/Native/Db.hs @@ -144,8 +144,6 @@ dbDefs = (funType tTyObjectAny [("table",tableTy)]) [LitExample "(describe-table accounts)"] "Get metadata for TABLE. Returns an object with 'name', 'hash', 'blessed', 'code', and 'keyset' fields." - ,setTopLevelOnly $ defGasRNative "describe-keyset" descKeySet - (funType tTyObjectAny [("keyset",tTyString)]) [] "Get metadata for KEYSET." ,setTopLevelOnly $ defRNative "describe-module" descModule (funType tTyObjectAny [("module",tTyString)]) [LitExample "(describe-module 'my-module)"] @@ -159,15 +157,6 @@ descTable _ [TTable {..}] = return $ toTObject TyAny def [ ("type", toTerm $ pack $ showPretty _tTableType)] descTable i as = argsError i as -descKeySet :: GasRNativeFun e -descKeySet g i [TLitString t] = do - r <- readRow (_faInfo i) KeySets (KeySetName t) - case r of - Just v -> computeGas' g i (GPostRead (ReadKeySet (KeySetName t) v)) $ - return $ toTerm v - Nothing -> evalError' i $ "Keyset not found: " <> pretty t -descKeySet _ i as = argsError i as - descModule :: RNativeFun e descModule i [TLitString t] = do mods <- lookupModule i (ModuleName t Nothing) diff --git a/src/Pact/Native/Keysets.hs b/src/Pact/Native/Keysets.hs index 8b687086c..f48c5790d 100644 --- a/src/Pact/Native/Keysets.hs +++ b/src/Pact/Native/Keysets.hs @@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} -- | -- Module : Pact.Native.Keysets @@ -13,10 +15,12 @@ module Pact.Native.Keysets where import Control.Lens +import Data.Default import Data.Text (Text) import Pact.Eval import Pact.Native.Internal +import Pact.Types.Pretty import Pact.Types.Purity import Pact.Types.Runtime @@ -49,6 +53,14 @@ keyDefs = "Define keyset as NAME with KEYSET, or if unspecified, read NAME from message payload as keyset, \ \similarly to 'read-keyset'. \ \If keyset NAME already exists, keyset will be enforced before updating to new value." + ,setTopLevelOnly $ defGasRNative "describe-keyset" descKeySet + (funType tTyObjectAny [("keyset",tTyString)]) + ["(env-data {\"k\": { \"pred\": \"keys-all\", \"keys\": [\"abc\"] }) (describe-keyset (read-keyset 'k))"] + "Get metadata for KEYSET." + ,setTopLevelOnly $ defGasRNative "describe-guard" descGuard + (funType tTyObjectAny [("guard",tTyString)]) + ["(env-data {'k: ['abc]}) (define-keyset 'ks (read-keyset 'k)) (describe-guard (keyset-ref-guard 'ks))"] + "Get metadata for GUARD." ,enforceGuardDef "enforce-keyset" ,defKeyPred KeysAll (==) ["(keys-all 3 3)"] "Keyset predicate function to match all keys in keyset." @@ -82,6 +94,58 @@ defineKeyset g0 fi as = case as of runSysOnly $ enforceKeySet i (Just ksn) oldKs writeRow i Write KeySets ksn ks & success "Keyset defined" +descKeySet :: GasRNativeFun e +descKeySet g i [TLitString t] = do + r <- readRow (_faInfo i) KeySets (KeySetName t) + case r of + Just v -> computeGas' g i (GPostRead (ReadKeySet (KeySetName t) v)) $ + return $ toTerm v + Nothing -> evalError' i $ "Keyset not found: " <> pretty t +descKeySet _ i as = argsError i as + +descGuard :: GasRNativeFun e +descGuard gas i [TGuard g _] = do + case g of + GKeySet ks -> computeGas' gas i (GUserApp Defun) $ + return $ toTObject TyAny def + [ ("type", tStr "KeySet") + , ("keyset", toTerm ks) + ] + GKeySetRef (KeySetName t) -> do + r <- readRow (_faInfo i) KeySets (KeySetName t) + case r of + Just v -> computeGas' gas i (GPostRead (ReadKeySet (KeySetName t) v)) $ + return $ toTObject TyAny def + [ ("type", tStr "KeySetRef") + , ("name", tStr t) + , ("keyset", toTerm v) + ] + Nothing -> evalError' i $ "Keyset not found: " <> pretty t + + --descKeySet gas i [TLiteral (LString t) info] + GPact (PactGuard (PactId pid) nm) -> computeGas' gas i (GUserApp Defun) $ + return $ toTObject TyAny def + [ ("type", tStr "Pact") + , ("name", tStr nm) + , ("pactId", tStr pid) + ] + GModule mg -> computeGas' gas i (GUserApp Defun) $ + return $ toTObject TyAny def + [ ("type", tStr "Module") + , ("module", toTObject TyAny def $ + ("name", tStr $ _mnName $ _mgModuleName mg) : + maybe [] (\(NamespaceName t) -> [("namespace", tStr t)]) (_mnNamespace $ _mgModuleName mg) + ) + , ("name", tStr $ _mgName mg) + ] + GUser ug -> computeGas' gas i (GUserApp Defun) $ + return $ toTObject TyAny def + [ ("type", tStr "User") + , ("fun", tStr $ renderCompactText $ _ugFun ug) -- Meh + , ("args", toTList TyAny def $ _ugArgs ug) -- ??? + ] +descGuard _ i as = argsError i as + keyPred :: (Integer -> Integer -> Bool) -> RNativeFun e keyPred predfun _ [TLitInteger count,TLitInteger matched] = return $ toTerm (predfun count matched)