diff --git a/pact/gas-payer/gas-payer-v1.repl b/pact/gas-payer/gas-payer-v1.repl index e243dbd2d6..e29a39da83 100644 --- a/pact/gas-payer/gas-payer-v1.repl +++ b/pact/gas-payer/gas-payer-v1.repl @@ -7,6 +7,7 @@ (env-data {'gas-payer-operate: ["operate"]}) (env-keys ["operate"]) +(env-exec-config ['DisablePact44]) (load "gas-payer-v1-reference.pact") (verify "user.gas-payer-v1-reference") diff --git a/pact/genesis/mainnet/ns.yaml b/pact/genesis/mainnet/ns.yaml index 37f71447ee..c5300c441d 100644 --- a/pact/genesis/mainnet/ns.yaml +++ b/pact/genesis/mainnet/ns.yaml @@ -1,4 +1,4 @@ -codeFile: ../../namespaces/ns.pact +codeFile: ../../namespaces/v1/ns.pact data: ns-admin-keyset: keys: diff --git a/pact/genesis/ns.yaml b/pact/genesis/ns.yaml index 6df5ac0572..ba9e527895 100644 --- a/pact/genesis/ns.yaml +++ b/pact/genesis/ns.yaml @@ -1,4 +1,4 @@ -codeFile: ../namespaces/ns.pact +codeFile: ../namespaces/v1/ns.pact data: ns-admin-keyset: ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] ns-operate-keyset: ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] diff --git a/pact/namespaces/ns.pact b/pact/namespaces/ns.pact index ac61e95090..908e8c101c 100644 --- a/pact/namespaces/ns.pact +++ b/pact/namespaces/ns.pact @@ -1,7 +1,3 @@ - -(define-keyset 'ns-admin-keyset (read-keyset 'ns-admin-keyset)) -(define-keyset 'ns-operate-keyset (read-keyset 'ns-genesis-keyset)) - (module ns GOVERNANCE "Administers definition of new namespaces in Chainweb." @@ -31,25 +27,48 @@ (enforce (is-charset CHARSET_LATIN1 name) "Name must be in latin1 charset")) + (defun create-principal-namespace:string + ( g:guard + ) + " Format principal namespace as Pact hash (BLAKE2b256) of principal \ + \ in hex truncated to 160 bits (40 characters), prepended with 'n_'.\ + \ Only w: and k: account protocols are supported. " + + (let + ((ty (typeof-principal (create-principal g)))) + + ;; only w: and k: currently supported + (if (or (= ty "k:") (= ty "w:")) + (+ "n_" (take 40 (int-to-str 16 (str-to-int 64 (hash g))))) + (enforce false + (format "Unsupported guard protocol: {}" [ty])) + )) + ) + (defun validate:bool ( ns-name:string ns-admin:guard ) - " Manages namespace install for Chainweb. Requires active row in registry \ + " Manages namespace install for Chainweb. \ + \ Allows principal namespaces. \ + \ Non-principal namespaces require active row in registry \ \ for NS-NAME with guard matching NS-ADMIN." - (validate-name ns-name) + (if (= (create-principal-namespace ns-admin) ns-name) + + true ;; allow principal namespaces - (with-default-read registry ns-name - { 'admin-guard : ns-admin - , 'active : false } - { 'admin-guard := ag - , 'active := is-active } + (with-default-read registry ns-name ;; otherwise enforce registry + { 'admin-guard : ns-admin + , 'active : false } + { 'admin-guard := ag + , 'active := is-active } (enforce is-active "Inactive or unregistered namespace") (enforce (= ns-admin ag) "Admin guard must match guard in registry") - true)) + true) + )) (defun write-registry:string ( ns-name:string @@ -73,20 +92,4 @@ ( ns-name:string ) (read registry ns-name)) - ) - -(create-table registry) - -(write-registry "kadena" - (keyset-ref-guard 'ns-operate-keyset) true) -(write-registry "user" GUARD_FAILURE true) -(write-registry "free" GUARD_FAILURE true) - -(define-namespace "kadena" - (keyset-ref-guard 'ns-operate-keyset) - (keyset-ref-guard 'ns-operate-keyset)) - -(define-namespace "user" GUARD_SUCCESS GUARD_FAILURE) -(define-namespace "free" GUARD_SUCCESS GUARD_FAILURE) -;;rotate to real operate keyset -(define-keyset 'ns-operate-keyset (read-keyset 'ns-operate-keyset)) +) diff --git a/pact/namespaces/ns.repl b/pact/namespaces/ns.repl index f841027556..c8093d0edb 100644 --- a/pact/namespaces/ns.repl +++ b/pact/namespaces/ns.repl @@ -1,16 +1,31 @@ (env-exec-config ["DisablePact44", "DisablePact45"]) (begin-tx) + (env-data { 'ns-admin-keyset: ["admin"] , 'ns-operate-keyset: ["operate"] , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) +(load "v1/ns.pact") + +(commit-tx) +(begin-tx) + +;; Test that we can upgrade ns.pact only when appropriate keys +;; are in scope. +(expect-failure + "Cannot upgrade the namespace contract due to governance failure" + "tx failure on upgrade" + (try (enforce false "tx failure on upgrade") (load "ns.pact"))) + +(env-keys ['admin]) (load "ns.pact") + (commit-tx) +(begin-tx) (env-namespace-policy false (ns.validate)) -(begin-tx) (namespace 'user) (env-keys []) @@ -67,6 +82,7 @@ (defcap G () (enforce false "disabled")) (defun foo () 4)) (commit-tx) +(begin-tx) (expect "kadena.mod2 works" 4 (kadena.mod2.foo)) @@ -85,3 +101,109 @@ (expect-failure "must be latin1 charset" (write-registry "emilyπ" GUARD_SUCCESS true)) + +(commit-tx) +(begin-tx) + +(env-exec-config []) +(env-data + { 'single : + ["70c787fcfe6c6f4ec23d13c2e94682bc90952f7cec06c7dbac1c012b0b6678b9"] + , 'multi : ["a", "b"] + }) + +(expect + "single principal ns" + "n_c1a583206e24450af26de41110042b019695db8c" + (ns.create-principal-namespace (read-keyset 'single))) + +(expect + "multi principal ns" + "n_64bfdef1c668b167c87f7cf329454c572e284664" + (ns.create-principal-namespace (read-keyset 'multi))) + +(expect-failure + "Principal of other than admin keyset fails" + "Inactive or unregistered namespace" + (define-namespace + "n_c1a583206e24450af26de41110042b019695db8c" + (read-keyset 'single) + (read-keyset 'multi)) +) + +(define-namespace + "n_c1a583206e24450af26de41110042b019695db8c" + (read-keyset 'single) + (read-keyset 'single)) + +(define-namespace + "n_64bfdef1c668b167c87f7cf329454c572e284664" + (read-keyset 'multi) + (read-keyset 'multi)) + +(commit-tx) + +(begin-tx "test rotation") +(env-keys + [ "70c787fcfe6c6f4ec23d13c2e94682bc90952f7cec06c7dbac1c012b0b6678b9" ]) +;; rotate to multi/multi +(define-namespace + "n_c1a583206e24450af26de41110042b019695db8c" + (read-keyset 'multi) + (read-keyset 'multi)) +;; rotate to multi/single +(env-keys ["a","b"]) +(define-namespace + "n_c1a583206e24450af26de41110042b019695db8c" + (read-keyset 'multi) + (read-keyset 'single)) +(rollback-tx) + + +(begin-tx) +(env-exec-config []) +(env-keys + [ "70c787fcfe6c6f4ec23d13c2e94682bc90952f7cec06c7dbac1c012b0b6678b9" + , "a" + , "b" + ] + ) + +(namespace "n_c1a583206e24450af26de41110042b019695db8c") +(module modK G + (defcap G () true) + (defun f () 1)) + +(namespace "n_64bfdef1c668b167c87f7cf329454c572e284664") +(module modW G + (defcap G () true) + (defun f () 2)) + +(commit-tx) +(begin-tx) + +(expect + "k: principal namespaces work" + 1 + (n_c1a583206e24450af26de41110042b019695db8c.modK.f)) + +(expect + "w: principal namespaces work" + 2 + (n_64bfdef1c668b167c87f7cf329454c572e284664.modW.f)) + +(commit-tx) +(begin-tx) + +(env-data + { "n_c1a583206e24450af26de41110042b019695db8c.failure": ['k] + }) + +(namespace "n_c1a583206e24450af26de41110042b019695db8c") +(define-keyset "n_c1a583206e24450af26de41110042b019695db8c.failure") + +(expect-failure + "r: principal namespaces do not work" + "Unsupported guard protocol: r:" + (ns.create-principal-namespace + (keyset-ref-guard "n_c1a583206e24450af26de41110042b019695db8c.failure"))) diff --git a/pact/namespaces/v1/ns.pact b/pact/namespaces/v1/ns.pact new file mode 100644 index 0000000000..ac61e95090 --- /dev/null +++ b/pact/namespaces/v1/ns.pact @@ -0,0 +1,92 @@ + +(define-keyset 'ns-admin-keyset (read-keyset 'ns-admin-keyset)) +(define-keyset 'ns-operate-keyset (read-keyset 'ns-genesis-keyset)) + +(module ns GOVERNANCE + "Administers definition of new namespaces in Chainweb." + + (defschema reg-entry + admin-guard:guard + active:bool) + + (deftable registry:{reg-entry}) + + (defcap GOVERNANCE () + (enforce-keyset 'ns-admin-keyset)) + + (defcap OPERATE () + (enforce-keyset 'ns-operate-keyset)) + + (defconst GUARD_SUCCESS (create-user-guard (success))) + (defconst GUARD_FAILURE (create-user-guard (failure))) + + (defun success () + true) + (defun failure () + (enforce false "Disabled")) + + (defun validate-name (name) + (enforce (!= "" name) "Empty name not allowed") + (enforce (< (length name) 64) "Name must be less than 64 characters long") + (enforce (is-charset CHARSET_LATIN1 name) + "Name must be in latin1 charset")) + + (defun validate:bool + ( ns-name:string + ns-admin:guard + ) + " Manages namespace install for Chainweb. Requires active row in registry \ + \ for NS-NAME with guard matching NS-ADMIN." + + (validate-name ns-name) + + (with-default-read registry ns-name + { 'admin-guard : ns-admin + , 'active : false } + { 'admin-guard := ag + , 'active := is-active } + + (enforce is-active "Inactive or unregistered namespace") + (enforce (= ns-admin ag) "Admin guard must match guard in registry") + + true)) + + (defun write-registry:string + ( ns-name:string + guard:guard + active:bool + ) + " Write entry with GUARD and ACTIVE into registry for NAME. \ + \ Guarded by operate keyset. " + + (with-capability (OPERATE) + + (validate-name ns-name) + + (write registry ns-name + { 'admin-guard: guard + , 'active: active }) + + "Register entry written")) + + (defun query:object{reg-entry} + ( ns-name:string ) + (read registry ns-name)) + + ) + +(create-table registry) + +(write-registry "kadena" + (keyset-ref-guard 'ns-operate-keyset) true) +(write-registry "user" GUARD_FAILURE true) +(write-registry "free" GUARD_FAILURE true) + +(define-namespace "kadena" + (keyset-ref-guard 'ns-operate-keyset) + (keyset-ref-guard 'ns-operate-keyset)) + +(define-namespace "user" GUARD_SUCCESS GUARD_FAILURE) +(define-namespace "free" GUARD_SUCCESS GUARD_FAILURE) +;;rotate to real operate keyset +(define-keyset 'ns-operate-keyset (read-keyset 'ns-operate-keyset)) diff --git a/pact/namespaces/v1/ns.repl b/pact/namespaces/v1/ns.repl new file mode 100644 index 0000000000..bcf3d9499f --- /dev/null +++ b/pact/namespaces/v1/ns.repl @@ -0,0 +1,87 @@ +(env-exec-config ["DisablePact44"]) +(begin-tx) +(env-data + { 'ns-admin-keyset: ["admin"] + , 'ns-operate-keyset: ["operate"] + , 'ns-genesis-keyset: { "keys": [], "pred": "="} }) + +(load "ns.pact") +(commit-tx) + +(env-namespace-policy false (ns.validate)) + +(begin-tx) +(namespace 'user) +(env-keys []) + +(module mod G + (defcap G () (enforce false "disabled")) + (defun foo () 1)) + +(namespace 'free) + +(module mod G + (defcap G () (enforce false "disabled")) + (defun foo () 2)) + +(expect-failure + "Cannot bring kadena ns into scope w/o operate admin" + (namespace 'kadena)) + +(env-keys ["operate"]) + +(namespace 'kadena) + +(module mod G + (defcap G () (enforce false "disabled")) + (defun foo () 3)) + +(commit-tx) + +(expect "user.mod works" 1 (user.mod.foo)) +(expect "free.mod works" 2 (free.mod.foo)) +(expect "kadena.mod works" 3 (kadena.mod.foo)) + +(begin-tx) +(env-keys ["operate"]) +(env-data + { 'ns-admin-keyset: ["admin"] + , 'ns-operate-keyset: ["operate"] }) + +(expect-failure "cannot redefine user" + (define-namespace 'user ns.GUARD_FAILURE ns.GUARD_FAILURE)) +(expect-failure "cannot redefine free" + (define-namespace 'free ns.GUARD_FAILURE ns.GUARD_FAILURE)) +(expect "can redefine kadena" + "Namespace defined: kadena" + (define-namespace 'kadena ns.GUARD_SUCCESS ns.GUARD_FAILURE)) + +(commit-tx) + +(begin-tx) +(env-keys []) + +(namespace 'kadena) + +(module mod2 G + (defcap G () (enforce false "disabled")) + (defun foo () 4)) +(commit-tx) + +(expect "kadena.mod2 works" 4 (kadena.mod2.foo)) + +(use ns) +(env-keys ["operate"]) +(expect-failure + "cannot register empty name" + (write-registry "" GUARD_SUCCESS true)) + +(expect-failure + "cannot register >64 length name" + (write-registry + "1234567890123456789012345678901234567890123456789012345678901234567890" + GUARD_SUCCESS true)) + +(expect-failure + "must be latin1 charset" + (write-registry "emilyπ" GUARD_SUCCESS true)) diff --git a/pact/namespaces/yaml/ns-v2.yaml b/pact/namespaces/yaml/ns-v2.yaml new file mode 100644 index 0000000000..14406f97fa --- /dev/null +++ b/pact/namespaces/yaml/ns-v2.yaml @@ -0,0 +1,5 @@ +codeFile: ../ns.pact +nonce: load-ns-v2-mainnet +signers: + - public: 56ff9e5c1b5ec3e98172a76aa3cdc03d3ec664f147267fd99eee4dd09763c853 + - public: a241430816f0112add2179e3e3939ebab5336ab024be990960e684e38eb87d80 diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index ad042ba907..f386bc5cd0 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -79,6 +79,12 @@ coinReplV4 = "pact/coin-contract/v4/coin-v4.repl" coinReplV5 :: FilePath coinReplV5 = "pact/coin-contract/v5/coin-v5.repl" +nsReplV1 :: FilePath +nsReplV1 = "pact/namespaces/v1/ns.repl" + +nsReplV2 :: FilePath +nsReplV2 = "pact/namespaces/ns.repl" + logger :: Logger #if DEBUG_TEST logger = newLogger alwaysLog "" @@ -102,7 +108,10 @@ tests = testGroup "Chainweb.Test.Pact.TransactionTests" , testCase "v4" (ccReplTests coinReplV4) , testCase "v5" (ccReplTests coinReplV5) ] - , testCase "Ns Repl Tests" (ccReplTests "pact/namespaces/ns.repl") + , testGroup "Namespace repl unit tests" + [ testCase "Ns-v1 repl tests" $ ccReplTests nsReplV1 + , testCase "Ns-v2 repl tests" $ ccReplTests nsReplV2 + ] , testCase "Payer Repl Tests" (ccReplTests "pact/gas-payer/gas-payer-v1.repl") ] , testGroup "Precompiled Statements Tests"