From 51322b790dda07cb561049019f9d71e2f83db0b1 Mon Sep 17 00:00:00 2001 From: anarcher Date: Mon, 31 Oct 2022 01:11:45 +0900 Subject: [PATCH 01/20] feat: r/system/names public functions --- examples/gno.land/r/system/names/names.gno | 59 ++++++++++++++----- .../gno.land/r/system/names/names_test.gno | 1 + examples/gno.land/r/system/names/utils.gno | 36 +++++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 examples/gno.land/r/system/names/names_test.gno create mode 100644 examples/gno.land/r/system/names/utils.gno diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index e1f479fc379..7d40efd6e3f 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -23,34 +23,65 @@ func Register(namespace string) { // - already exists / reserved. // - min/max length, format. // - fees (dynamic, based on length). - panic("not implemented") + if existsNamespace(namespace) { + panic("namespace already exists") + } + + caller := std.GetOrigCaller() + namespaces.Set(namespace, Space{ + Admins: []std.Address{caller}, + }) } func AddAdmin(namespace string, newAdmin std.Address) { - // TODO: assertIsAdmin() - panic("not implemented") + assertIsAdmin(namespace) + + space := getSpace(namespace) + if !containsAddress(space.Admins, newAdmin) { + space.Admins = append(space.Admins, newAdmin) + } } func RemoveAdmin(namespace string, newAdmin std.Address) { - // TODO: assertIsAdmin() // TODO: check if self. - panic("not implemented") + assertIsAdmin(namespace) + + // remove admin + space := getSpace(namespace) + for i, admin := range space.Admins { + if admin == newAdmin { + space.Admins = append(space.Admins[:i], space.Admins[i+1:]...) + break + } + } } -func AddEditor(namespace string, newAdmin std.Address) { - // TODO: assertIsAdmin() - panic("not implemented") +func AddEditor(namespace string, newEditor std.Address) { + assertIsAdmin(namespace) + space := getSpace(namespace) + + if !containsAddress(space.Editors, newEditor) { + space.Editors = append(space.Editors, newEditor) + } } -func RemoveEditor(namespace string, newAdmin std.Address) { - // TODO: assertIsAdmin() - // TODO: check if self. - panic("not implemented") +func RemoveEditor(namespace string, newEditor std.Address) { + assertIsAdmin(namespace) + + // remove editor + space := getSpace(namespace) + for i, editor := range space.Editors { + if editor == newEditor { + space.Editors = append(space.Editors[:i], space.Editors[i+1:]...) + break + } + } } func SetInPause(namespace string, state bool) { - // TODO: assertIsAdmin() - panic("not implemented") + assertIsAdmin(namespace) + space := getSpace(namespace) + space.InPause = state } func Render(path string) string { diff --git a/examples/gno.land/r/system/names/names_test.gno b/examples/gno.land/r/system/names/names_test.gno new file mode 100644 index 00000000000..33577bef6a7 --- /dev/null +++ b/examples/gno.land/r/system/names/names_test.gno @@ -0,0 +1 @@ +package names diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno new file mode 100644 index 00000000000..d8fcfef02f2 --- /dev/null +++ b/examples/gno.land/r/system/names/utils.gno @@ -0,0 +1,36 @@ +package names + +import ( + "std" +) + +func getSpace(namespace string) Space { + s, ok := namespaces.Get(namespace) + if !ok { + panic("namespace does not exist") + } + return s.(Space) +} + +func existsNamespace(name string) bool { + _, ok := namespaces.Get(name) + return ok +} + +func assertIsAdmin(namespace string) { + caller := std.GetOrigCaller() + space := getSpace(namespace) + + if !containsAddress(space.Admins, caller) { + panic("Only admins can call this function") + } +} + +func containsAddress(addrs []std.Address, addr std.Address) bool { + for _, a := range addrs { + if a == addr { + return true + } + } + return false +} From 2a9fee62de7b9e3d2d67c3d30fdf35b6d98dd174 Mon Sep 17 00:00:00 2001 From: anarcher Date: Tue, 1 Nov 2022 23:05:14 +0900 Subject: [PATCH 02/20] test: names basic test --- examples/gno.land/r/system/names/names.gno | 8 ++- .../gno.land/r/system/names/names_test.gno | 49 +++++++++++++++++++ examples/gno.land/r/system/names/utils.gno | 4 +- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 7d40efd6e3f..058728e0665 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -28,8 +28,9 @@ func Register(namespace string) { } caller := std.GetOrigCaller() - namespaces.Set(namespace, Space{ - Admins: []std.Address{caller}, + namespaces.Set(namespace, &Space{ + Admins: []std.Address{caller}, + InPause: true, }) } @@ -48,6 +49,9 @@ func RemoveAdmin(namespace string, newAdmin std.Address) { // remove admin space := getSpace(namespace) + if len(space.Admins) == 1 { + panic("namespace at least needs one admin") + } for i, admin := range space.Admins { if admin == newAdmin { space.Admins = append(space.Admins[:i], space.Admins[i+1:]...) diff --git a/examples/gno.land/r/system/names/names_test.gno b/examples/gno.land/r/system/names/names_test.gno index 33577bef6a7..5294c47d715 100644 --- a/examples/gno.land/r/system/names/names_test.gno +++ b/examples/gno.land/r/system/names/names_test.gno @@ -1 +1,50 @@ package names + +import ( + "std" + "testing" +) + +func TestRegisterNamespace(t *testing.T) { + std.TestSetOrigCaller(std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")) + creator := std.GetOrigCaller() + + n := "test2" + Register(n) + s := getSpace(n) + assertTrue(t, containsAddress(s.Admins, creator)) +} + +func TestAddAdmins(t *testing.T) { + std.TestSetOrigCaller(std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")) + creator := std.GetOrigCaller() + + test1 := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + test2 := std.Address("g1r6casl322p5adkmmjjkjea404e7f6w29y6gzwg") + + ns := "test3" + + Register(ns) + s := getSpace(ns) + + AddAdmin(ns, test1) + AddAdmin(ns, test2) + assertTrue(t, containsAddress(s.Admins, test1)) + assertTrue(t, containsAddress(s.Admins, test2)) + + RemoveAdmin(ns, test1) + assertTrue(t, !containsAddress(s.Admins, test1)) + assertTrue(t, containsAddress(s.Admins, test2)) + + AddEditor(ns, test1) + assertTrue(t, containsAddress(s.Editors, test1)) + + RemoveEditor(ns, test1) + assertTrue(t, !containsAddress(s.Editors, test1)) +} + +func assertTrue(t *testing.T, b bool) { + if !b { + t.Fail() + } +} diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno index d8fcfef02f2..e95cd013680 100644 --- a/examples/gno.land/r/system/names/utils.gno +++ b/examples/gno.land/r/system/names/utils.gno @@ -4,12 +4,12 @@ import ( "std" ) -func getSpace(namespace string) Space { +func getSpace(namespace string) *Space { s, ok := namespaces.Get(namespace) if !ok { panic("namespace does not exist") } - return s.(Space) + return s.(*Space) } func existsNamespace(name string) bool { From db517fd79ceb30e8f47dbadfd852918225b63878 Mon Sep 17 00:00:00 2001 From: anarcher Date: Wed, 2 Nov 2022 00:07:15 +0900 Subject: [PATCH 03/20] chore: default inpause false --- examples/gno.land/r/system/names/names.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 058728e0665..4b5d32a696b 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -29,8 +29,7 @@ func Register(namespace string) { caller := std.GetOrigCaller() namespaces.Set(namespace, &Space{ - Admins: []std.Address{caller}, - InPause: true, + Admins: []std.Address{caller}, }) } From 65fbc4dc0bca2223423dad5652fb090a1160116e Mon Sep 17 00:00:00 2001 From: anarcher Date: Thu, 3 Nov 2022 00:52:22 +0900 Subject: [PATCH 04/20] feat(render): render index and namespace --- examples/gno.land/r/system/names/names.gno | 53 +++++++++++++++++++++- examples/gno.land/r/system/names/utils.gno | 7 +++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 4b5d32a696b..82982830c35 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -5,6 +5,7 @@ import ( "std" "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" ) // "AddPkg" will check if r/system/names exists. If yes, it will @@ -88,7 +89,55 @@ func SetInPause(namespace string, state bool) { } func Render(path string) string { - // TODO: by namespace. // TODO: by address. - return "not implemented" + + if path == "" { + return renderIndex() + } else if path[:2] == "n/" { + return renderNamespace(path[2:]) + } + return "" +} + +func renderNamespace(namespace string) string { + space := getSpace(namespace) + output := ufmt.Sprintf(` +# %s + +## Admins + +%s + +## Editors + +%s + +## InPause + +%s + +`, namespace, renderAddresses(space.Admins), renderAddresses(space.Editors), formatBool(space.InPause)) + return output + +} + +func renderIndex() string { + output := "## Namespaces \n" + namespaces.Iterate("", "", func(n *avl.Tree) bool { + namespace := n.Key() + space := n.Value().(*Space) + output += ufmt.Sprintf("* [%s](/r/system/names:n/%s) - admins: %d editors: %d inPause: %s \n", + namespace, namespace, len(space.Admins), len(space.Editors), formatBool(space.InPause)) + return false + }) + + return output +} + +func renderAddresses(addresses []std.Address) string { + output := "" + for _, address := range addresses { + output += ufmt.Sprintf("* %s \n", string(address)) + } + return output } diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno index e95cd013680..3f19d9d94db 100644 --- a/examples/gno.land/r/system/names/utils.gno +++ b/examples/gno.land/r/system/names/utils.gno @@ -34,3 +34,10 @@ func containsAddress(addrs []std.Address, addr std.Address) bool { } return false } + +func formatBool(b bool) string { + if b { + return "true" + } + return "false" +} From 180fb33a5d4734297a736cef38e6de590800fe82 Mon Sep 17 00:00:00 2001 From: anarcher Date: Sat, 5 Nov 2022 01:11:47 +0900 Subject: [PATCH 05/20] feat: reName and HasPerm --- examples/gno.land/r/system/names/names.gno | 44 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 82982830c35..9b43e753d6e 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -2,6 +2,8 @@ package names import ( + "errors" + "regexp" "std" "gno.land/p/demo/avl" @@ -13,6 +15,9 @@ import ( // determine if an address can publish a package or not. var namespaces avl.Tree // name(string) -> Space +// TODO: more accurate. +var reNamespace = regexp.MustCompile(`^[a-z][a-z0-9_]{2,30}$`) + type Space struct { Admins []std.Address Editors []std.Address @@ -28,6 +33,10 @@ func Register(namespace string) { panic("namespace already exists") } + if err := validateNamespace(namespace); err != nil { + panic(err) + } + caller := std.GetOrigCaller() namespaces.Set(namespace, &Space{ Admins: []std.Address{caller}, @@ -52,12 +61,14 @@ func RemoveAdmin(namespace string, newAdmin std.Address) { if len(space.Admins) == 1 { panic("namespace at least needs one admin") } + var admins []std.Address for i, admin := range space.Admins { if admin == newAdmin { - space.Admins = append(space.Admins[:i], space.Admins[i+1:]...) + admins = append(space.Admins[:i], space.Admins[i+1:]...) break } } + space.Admins = admins } func AddEditor(namespace string, newEditor std.Address) { @@ -74,12 +85,14 @@ func RemoveEditor(namespace string, newEditor std.Address) { // remove editor space := getSpace(namespace) + var editors []std.Address for i, editor := range space.Editors { if editor == newEditor { - space.Editors = append(space.Editors[:i], space.Editors[i+1:]...) + editors = append(space.Editors[:i], space.Editors[i+1:]...) break } } + space.Editors = editors } func SetInPause(namespace string, state bool) { @@ -88,6 +101,26 @@ func SetInPause(namespace string, state bool) { space.InPause = state } +// HasPerm returns true if the caller has permission of the namespace. +func HasPerm(namespace string) bool { + caller := std.GetOrigCaller() + space := getSpace(namespace) + + if space.InPause { + return false + } + + if containsAddress(space.Admins, caller) { + return true + } + + if containsAddress(space.Editors, caller) { + return true + } + + return false +} + func Render(path string) string { // TODO: by address. @@ -141,3 +174,10 @@ func renderAddresses(addresses []std.Address) string { } return output } + +func validateNamespace(namespace string) error { + if !reNamespace.MatchString(namespace) { + return errors.New("invalid namespace") + } + return nil +} From 32a086fe22d2a0ab77013d9042f953539e7241a1 Mon Sep 17 00:00:00 2001 From: anarcher Date: Sat, 5 Nov 2022 15:53:11 +0900 Subject: [PATCH 06/20] feat: check namespace perm in vmkeeper.AddPackage --- tm2/pkg/sdk/vm/keeper.go | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/tm2/pkg/sdk/vm/keeper.go b/tm2/pkg/sdk/vm/keeper.go index 28531f0a773..a251effd8ca 100644 --- a/tm2/pkg/sdk/vm/keeper.go +++ b/tm2/pkg/sdk/vm/keeper.go @@ -9,6 +9,7 @@ import ( gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/stdlibs" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" @@ -128,6 +129,35 @@ func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store { } } +func (vm *VMKeeper) checkNamespacePerm(ctx sdk.Context, creator crypto.Address, pkgPath string) error { + store := vm.getGnoStore(ctx) + + // if r/system/names does not exists -> skip validation. + if pv := store.GetPackage("gno.land/r/system/names", false); pv == nil { + return nil + } + + pathSp := strings.SplitN(pkgPath, "/", 4) // gno.land/r/... + namespace := pathSp[2] + + res, err := vm.Call(ctx, MsgCall{ + Caller: creator, + Send: std.Coins{}, + PkgPath: "gno.land/r/system/names", + Func: "HasPerm", + Args: []string{namespace}, + }) + if err != nil { + return err + } + //TODO: needs fixed representation of bool + if res != "(true bool)" { + return fmt.Errorf("namespace %q not allowed", namespace) + } + + return nil +} + // AddPackage adds a package with given fileset. func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { creator := msg.Creator @@ -153,12 +183,9 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { // Pay deposit from creator. pkgAddr := gno.DerivePkgAddr(pkgPath) - // TODO: ACLs. - // - if r/system/names does not exists -> skip validation. - // - loads r/system/names data state. - // - lookup r/system/names.namespaces for `{r,p}/NAMES`. - // - check if caller is in Admins or Editors. - // - check if namespace is not in pause. + if err := vm.checkNamespacePerm(ctx, creator, pkgPath); err != nil { + return err + } err := vm.bank.SendCoins(ctx, creator, pkgAddr, deposit) if err != nil { From 3b865ef1a5ac6cb955ffe6f67dc4bbd8927d03a2 Mon Sep 17 00:00:00 2001 From: anarcher Date: Sat, 5 Nov 2022 16:36:45 +0900 Subject: [PATCH 07/20] refactor: separate space methods and public function --- examples/gno.land/r/system/names/names.gno | 104 +++++++++++---------- examples/gno.land/r/system/names/utils.gno | 16 ++++ 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 9b43e753d6e..8c25b5fbb52 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -24,6 +24,49 @@ type Space struct { InPause bool } +func (s *Space) addAdmin(newAdmin std.Address) { + if !containsAddress(s.Admins, newAdmin) { + s.Admins = append(s.Admins, newAdmin) + } +} + +func (s *Space) removeAdmin(admin std.Address) error { + if len(s.Admins) == 1 { + return errors.New("namespace at least needs one admin") + } + admins := removeAddress(s.Admins, admin) + s.Admins = admins + return nil +} + +func (s *Space) addEditor(newEditor std.Address) { + if !containsAddress(s.Editors, newEditor) { + s.Editors = append(s.Editors, newEditor) + } +} + +func (s *Space) removeEditor(editor std.Address) error { + editors := removeAddress(s.Editors, editor) + s.Editors = editors + return nil +} + +func (s *Space) hasPerm(caller std.Address) bool { + if s.InPause { + return false + } + + if containsAddress(s.Admins, caller) { + return true + } + + if containsAddress(s.Editors, caller) { + return true + } + + return false +} + func Register(namespace string) { // TODO: input sanitization: // - already exists / reserved. @@ -45,54 +88,28 @@ func Register(namespace string) { func AddAdmin(namespace string, newAdmin std.Address) { assertIsAdmin(namespace) - space := getSpace(namespace) - if !containsAddress(space.Admins, newAdmin) { - space.Admins = append(space.Admins, newAdmin) - } + space.addAdmin(newAdmin) } -func RemoveAdmin(namespace string, newAdmin std.Address) { - // TODO: check if self. +func RemoveAdmin(namespace string, admin std.Address) { assertIsAdmin(namespace) - - // remove admin space := getSpace(namespace) - if len(space.Admins) == 1 { - panic("namespace at least needs one admin") - } - var admins []std.Address - for i, admin := range space.Admins { - if admin == newAdmin { - admins = append(space.Admins[:i], space.Admins[i+1:]...) - break - } - } - space.Admins = admins + err := space.removeAdmin(admin) + checkErr(err) } func AddEditor(namespace string, newEditor std.Address) { assertIsAdmin(namespace) space := getSpace(namespace) - - if !containsAddress(space.Editors, newEditor) { - space.Editors = append(space.Editors, newEditor) - } + space.addEditor(newEditor) } -func RemoveEditor(namespace string, newEditor std.Address) { +func RemoveEditor(namespace string, editor std.Address) { assertIsAdmin(namespace) - - // remove editor space := getSpace(namespace) - var editors []std.Address - for i, editor := range space.Editors { - if editor == newEditor { - editors = append(space.Editors[:i], space.Editors[i+1:]...) - break - } - } - space.Editors = editors + err := space.removeEditor(editor) + checkErr(err) } func SetInPause(namespace string, state bool) { @@ -102,23 +119,14 @@ func SetInPause(namespace string, state bool) { } // HasPerm returns true if the caller has permission of the namespace. +// If the namespace does not exist, it will return panic. +// If the namespace exists but the caller is not an admin or editor, +// it will return false. +// The vm keeper will use this function to check to add package func HasPerm(namespace string) bool { caller := std.GetOrigCaller() space := getSpace(namespace) - - if space.InPause { - return false - } - - if containsAddress(space.Admins, caller) { - return true - } - - if containsAddress(space.Editors, caller) { - return true - } - - return false + return space.hasPerm(caller) } func Render(path string) string { diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno index 3f19d9d94db..b44341fe33f 100644 --- a/examples/gno.land/r/system/names/utils.gno +++ b/examples/gno.land/r/system/names/utils.gno @@ -26,6 +26,16 @@ func assertIsAdmin(namespace string) { } } +func removeAddress(addrs []std.Address, addr std.Address) []std.Address { + var newAddrs []std.Address + for _, a := range addrs { + if a != addr { + newAddrs = append(newAddrs, a) + } + } + return newAddrs +} + func containsAddress(addrs []std.Address, addr std.Address) bool { for _, a := range addrs { if a == addr { @@ -35,6 +45,12 @@ func containsAddress(addrs []std.Address, addr std.Address) bool { return false } +func checkErr(err error) { + if err != nil { + panic(err) + } +} + func formatBool(b bool) string { if b { return "true" From 5e18c066efac4da8a1b7ccabf7998cf3122caae0 Mon Sep 17 00:00:00 2001 From: anarcher Date: Sat, 5 Nov 2022 22:16:18 +0900 Subject: [PATCH 08/20] refactor: assertIsAdmin --- examples/gno.land/r/system/names/names.gno | 17 ++++++++++++----- examples/gno.land/r/system/names/utils.gno | 6 ++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 8c25b5fbb52..d3604788216 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -24,6 +24,13 @@ type Space struct { InPause bool } +func (s *Space) isAdmin(addr std.Address) bool { + if containsAddress(s.Admins, addr) { + return true + } + return false +} + func (s *Space) addAdmin(newAdmin std.Address) { if !containsAddress(s.Admins, newAdmin) { s.Admins = append(s.Admins, newAdmin) @@ -87,34 +94,34 @@ func Register(namespace string) { } func AddAdmin(namespace string, newAdmin std.Address) { - assertIsAdmin(namespace) space := getSpace(namespace) + assertIsAdmin(space) space.addAdmin(newAdmin) } func RemoveAdmin(namespace string, admin std.Address) { - assertIsAdmin(namespace) space := getSpace(namespace) + assertIsAdmin(space) err := space.removeAdmin(admin) checkErr(err) } func AddEditor(namespace string, newEditor std.Address) { - assertIsAdmin(namespace) space := getSpace(namespace) + assertIsAdmin(space) space.addEditor(newEditor) } func RemoveEditor(namespace string, editor std.Address) { - assertIsAdmin(namespace) space := getSpace(namespace) + assertIsAdmin(space) err := space.removeEditor(editor) checkErr(err) } func SetInPause(namespace string, state bool) { - assertIsAdmin(namespace) space := getSpace(namespace) + assertIsAdmin(space) space.InPause = state } diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno index b44341fe33f..8215ddc5b5a 100644 --- a/examples/gno.land/r/system/names/utils.gno +++ b/examples/gno.land/r/system/names/utils.gno @@ -17,11 +17,9 @@ func existsNamespace(name string) bool { return ok } -func assertIsAdmin(namespace string) { +func assertIsAdmin(space *Space) { caller := std.GetOrigCaller() - space := getSpace(namespace) - - if !containsAddress(space.Admins, caller) { + if !space.isAdmin(caller) { panic("Only admins can call this function") } } From 4a3c896bae50a81be6df7dce05695635467dfcb2 Mon Sep 17 00:00:00 2001 From: "myoung-su,shin" Date: Thu, 10 Nov 2022 15:47:40 +0900 Subject: [PATCH 09/20] Update examples/gno.land/r/system/names/names.gno Thanks! :-) Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/r/system/names/names.gno | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index d3604788216..0d39827b0b6 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -25,10 +25,7 @@ type Space struct { } func (s *Space) isAdmin(addr std.Address) bool { - if containsAddress(s.Admins, addr) { - return true - } - return false + return containsAddress(s.Admins, addr) } func (s *Space) addAdmin(newAdmin std.Address) { From 2e9e7a54298a2f8492510243418cf04d6b98ab8e Mon Sep 17 00:00:00 2001 From: "myoung-su,shin" Date: Thu, 10 Nov 2022 15:49:07 +0900 Subject: [PATCH 10/20] Update examples/gno.land/r/system/names/names.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/r/system/names/names.gno | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 0d39827b0b6..65b2cc9c766 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -80,9 +80,8 @@ func Register(namespace string) { panic("namespace already exists") } - if err := validateNamespace(namespace); err != nil { - panic(err) - } + err := validateNamespace(namespace) + checkErr(err) caller := std.GetOrigCaller() namespaces.Set(namespace, &Space{ From a0a83e8ea4fbc86b589cee0fa6ce43239b8c7134 Mon Sep 17 00:00:00 2001 From: anarcher Date: Fri, 11 Nov 2022 01:57:39 +0900 Subject: [PATCH 11/20] refactor: move public functions to public.gno --- examples/gno.land/r/system/names/names.gno | 116 ----------------- examples/gno.land/r/system/names/public.gno | 123 ++++++++++++++++++ .../names/{names_test.gno => public_test.gno} | 0 3 files changed, 123 insertions(+), 116 deletions(-) create mode 100644 examples/gno.land/r/system/names/public.gno rename examples/gno.land/r/system/names/{names_test.gno => public_test.gno} (100%) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index 65b2cc9c766..b84a76b7f68 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -7,7 +7,6 @@ import ( "std" "gno.land/p/demo/avl" - "gno.land/p/demo/ufmt" ) // "AddPkg" will check if r/system/names exists. If yes, it will @@ -71,121 +70,6 @@ func (s *Space) hasPerm(caller std.Address) bool { return false } -func Register(namespace string) { - // TODO: input sanitization: - // - already exists / reserved. - // - min/max length, format. - // - fees (dynamic, based on length). - if existsNamespace(namespace) { - panic("namespace already exists") - } - - err := validateNamespace(namespace) - checkErr(err) - - caller := std.GetOrigCaller() - namespaces.Set(namespace, &Space{ - Admins: []std.Address{caller}, - }) -} - -func AddAdmin(namespace string, newAdmin std.Address) { - space := getSpace(namespace) - assertIsAdmin(space) - space.addAdmin(newAdmin) -} - -func RemoveAdmin(namespace string, admin std.Address) { - space := getSpace(namespace) - assertIsAdmin(space) - err := space.removeAdmin(admin) - checkErr(err) -} - -func AddEditor(namespace string, newEditor std.Address) { - space := getSpace(namespace) - assertIsAdmin(space) - space.addEditor(newEditor) -} - -func RemoveEditor(namespace string, editor std.Address) { - space := getSpace(namespace) - assertIsAdmin(space) - err := space.removeEditor(editor) - checkErr(err) -} - -func SetInPause(namespace string, state bool) { - space := getSpace(namespace) - assertIsAdmin(space) - space.InPause = state -} - -// HasPerm returns true if the caller has permission of the namespace. -// If the namespace does not exist, it will return panic. -// If the namespace exists but the caller is not an admin or editor, -// it will return false. -// The vm keeper will use this function to check to add package -func HasPerm(namespace string) bool { - caller := std.GetOrigCaller() - space := getSpace(namespace) - return space.hasPerm(caller) -} - -func Render(path string) string { - // TODO: by address. - - if path == "" { - return renderIndex() - } else if path[:2] == "n/" { - return renderNamespace(path[2:]) - } - return "" -} - -func renderNamespace(namespace string) string { - space := getSpace(namespace) - output := ufmt.Sprintf(` -# %s - -## Admins - -%s - -## Editors - -%s - -## InPause - -%s - -`, namespace, renderAddresses(space.Admins), renderAddresses(space.Editors), formatBool(space.InPause)) - return output - -} - -func renderIndex() string { - output := "## Namespaces \n" - namespaces.Iterate("", "", func(n *avl.Tree) bool { - namespace := n.Key() - space := n.Value().(*Space) - output += ufmt.Sprintf("* [%s](/r/system/names:n/%s) - admins: %d editors: %d inPause: %s \n", - namespace, namespace, len(space.Admins), len(space.Editors), formatBool(space.InPause)) - return false - }) - - return output -} - -func renderAddresses(addresses []std.Address) string { - output := "" - for _, address := range addresses { - output += ufmt.Sprintf("* %s \n", string(address)) - } - return output -} - func validateNamespace(namespace string) error { if !reNamespace.MatchString(namespace) { return errors.New("invalid namespace") diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno new file mode 100644 index 00000000000..a2bce902427 --- /dev/null +++ b/examples/gno.land/r/system/names/public.gno @@ -0,0 +1,123 @@ +package names + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +func Register(namespace string) { + // TODO: input sanitization: + // - already exists / reserved. + // - min/max length, format. + // - fees (dynamic, based on length). + if existsNamespace(namespace) { + panic("namespace already exists") + } + + err := validateNamespace(namespace) + checkErr(err) + + caller := std.GetOrigCaller() + namespaces.Set(namespace, &Space{ + Admins: []std.Address{caller}, + }) +} + +func AddAdmin(namespace string, newAdmin std.Address) { + space := getSpace(namespace) + assertIsAdmin(space) + space.addAdmin(newAdmin) +} + +func RemoveAdmin(namespace string, admin std.Address) { + space := getSpace(namespace) + assertIsAdmin(space) + err := space.removeAdmin(admin) + checkErr(err) +} + +func AddEditor(namespace string, newEditor std.Address) { + space := getSpace(namespace) + assertIsAdmin(space) + space.addEditor(newEditor) +} + +func RemoveEditor(namespace string, editor std.Address) { + space := getSpace(namespace) + assertIsAdmin(space) + err := space.removeEditor(editor) + checkErr(err) +} + +func SetInPause(namespace string, state bool) { + space := getSpace(namespace) + assertIsAdmin(space) + space.InPause = state +} + +// HasPerm returns true if the caller has permission of the namespace. +// If the namespace does not exist, it will return panic. +// If the namespace exists but the caller is not an admin or editor, +// it will return false. +// The vm keeper will use this function to check to add package +func HasPerm(namespace string) bool { + caller := std.GetOrigCaller() + space := getSpace(namespace) + return space.hasPerm(caller) +} + +func Render(path string) string { + // TODO: by address. + + if path == "" { + return renderIndex() + } else if path[:2] == "n/" { + return renderNamespace(path[2:]) + } + return "" +} + +func renderNamespace(namespace string) string { + space := getSpace(namespace) + output := ufmt.Sprintf(` +# %s + +## Admins + +%s + +## Editors + +%s + +## InPause + +%s + +`, namespace, renderAddresses(space.Admins), renderAddresses(space.Editors), formatBool(space.InPause)) + return output + +} + +func renderIndex() string { + output := "## Namespaces \n" + namespaces.Iterate("", "", func(n *avl.Tree) bool { + namespace := n.Key() + space := n.Value().(*Space) + output += ufmt.Sprintf("* [%s](/r/system/names:n/%s) - admins: %d editors: %d inPause: %s \n", + namespace, namespace, len(space.Admins), len(space.Editors), formatBool(space.InPause)) + return false + }) + + return output +} + +func renderAddresses(addresses []std.Address) string { + output := "" + for _, address := range addresses { + output += ufmt.Sprintf("* %s \n", string(address)) + } + return output +} diff --git a/examples/gno.land/r/system/names/names_test.gno b/examples/gno.land/r/system/names/public_test.gno similarity index 100% rename from examples/gno.land/r/system/names/names_test.gno rename to examples/gno.land/r/system/names/public_test.gno From 32c816a8cddd7ed212fb4569780ae591205b1011 Mon Sep 17 00:00:00 2001 From: anarcher Date: Fri, 11 Nov 2022 02:00:20 +0900 Subject: [PATCH 12/20] chore: comment TODO: prevent self --- examples/gno.land/r/system/names/public.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index a2bce902427..4c94d4f6abe 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -34,6 +34,7 @@ func AddAdmin(namespace string, newAdmin std.Address) { func RemoveAdmin(namespace string, admin std.Address) { space := getSpace(namespace) assertIsAdmin(space) + // TODO: prevent self err := space.removeAdmin(admin) checkErr(err) } From 5f1c6c4e8aa8330e395828eeed35dd9e2d705cad Mon Sep 17 00:00:00 2001 From: anarcher Date: Mon, 16 Jan 2023 23:11:02 +0900 Subject: [PATCH 13/20] feat: caller can't remove self --- examples/gno.land/r/system/names/names.gno | 6 ++++ .../gno.land/r/system/names/public_test.gno | 29 +++++++++++++++++-- examples/gno.land/r/system/names/utils.gno | 7 +++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/system/names/names.gno b/examples/gno.land/r/system/names/names.gno index b84a76b7f68..265c5e43779 100644 --- a/examples/gno.land/r/system/names/names.gno +++ b/examples/gno.land/r/system/names/names.gno @@ -37,6 +37,9 @@ func (s *Space) removeAdmin(admin std.Address) error { if len(s.Admins) == 1 { return errors.New("namespace at least needs one admin") } + if isCallerAddress(admin) { + return errors.New("cannot remove self") + } admins := removeAddress(s.Admins, admin) s.Admins = admins return nil @@ -49,6 +52,9 @@ func (s *Space) addEditor(newEditor std.Address) { } func (s *Space) removeEditor(editor std.Address) error { + if isCallerAddress(editor) { + return errors.New("cannot remove self") + } editors := removeAddress(s.Editors, editor) s.Editors = editors return nil diff --git a/examples/gno.land/r/system/names/public_test.gno b/examples/gno.land/r/system/names/public_test.gno index 5294c47d715..0d8de50519a 100644 --- a/examples/gno.land/r/system/names/public_test.gno +++ b/examples/gno.land/r/system/names/public_test.gno @@ -15,7 +15,7 @@ func TestRegisterNamespace(t *testing.T) { assertTrue(t, containsAddress(s.Admins, creator)) } -func TestAddAdmins(t *testing.T) { +func TestAdminFuncs(t *testing.T) { std.TestSetOrigCaller(std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")) creator := std.GetOrigCaller() @@ -33,7 +33,7 @@ func TestAddAdmins(t *testing.T) { assertTrue(t, containsAddress(s.Admins, test2)) RemoveAdmin(ns, test1) - assertTrue(t, !containsAddress(s.Admins, test1)) + assertFalse(t, containsAddress(s.Admins, test1)) assertTrue(t, containsAddress(s.Admins, test2)) AddEditor(ns, test1) @@ -43,8 +43,33 @@ func TestAddAdmins(t *testing.T) { assertTrue(t, !containsAddress(s.Editors, test1)) } +func TestSpaceMethods(t *testing.T) { + std.TestSetOrigCaller(std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")) + creator := std.GetOrigCaller() + + test1 := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + test2 := std.Address("g1r6casl322p5adkmmjjkjea404e7f6w29y6gzwg") + + ns := "test4" + Register(ns) + s := getSpace(ns) + + assertTrue(t, s.isAdmin(creator)) + assertTrue(t, s.removeAdmin(creator) != nil) + + s.addAdmin(test1) + assertTrue(t, s.removeAdmin(test1) == nil) + +} + func assertTrue(t *testing.T, b bool) { if !b { t.Fail() } } + +func assertFalse(t *testing.T, b bool) { + if b { + t.Fail() + } +} diff --git a/examples/gno.land/r/system/names/utils.gno b/examples/gno.land/r/system/names/utils.gno index 8215ddc5b5a..5774c1adde5 100644 --- a/examples/gno.land/r/system/names/utils.gno +++ b/examples/gno.land/r/system/names/utils.gno @@ -43,6 +43,13 @@ func containsAddress(addrs []std.Address, addr std.Address) bool { return false } +func isCallerAddress(addr std.Address) bool { + if addr == std.GetOrigCaller() { + return true + } + return false +} + func checkErr(err error) { if err != nil { panic(err) From 905a45859e040179c2aa30031070763314e46ce5 Mon Sep 17 00:00:00 2001 From: anarcher Date: Mon, 16 Jan 2023 23:34:55 +0900 Subject: [PATCH 14/20] feat: global variable `enabled` for skipping to check perms if adding pkg --- examples/gno.land/r/system/names/enable.gno | 5 +++++ examples/gno.land/r/system/names/public.gno | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 examples/gno.land/r/system/names/enable.gno diff --git a/examples/gno.land/r/system/names/enable.gno b/examples/gno.land/r/system/names/enable.gno new file mode 100644 index 00000000000..b8cba4f892c --- /dev/null +++ b/examples/gno.land/r/system/names/enable.gno @@ -0,0 +1,5 @@ +package names + +// if enabled is true, `HasPerm` will check the permissions of names. +// todo: this is a temporary solution, we should use a more robust +var enabled bool = false diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index 4c94d4f6abe..5cddb736fb4 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -64,6 +64,10 @@ func SetInPause(namespace string, state bool) { // it will return false. // The vm keeper will use this function to check to add package func HasPerm(namespace string) bool { + // if enabled is false, it always returns true for dev and testing. + if enabled == false { + return true + } caller := std.GetOrigCaller() space := getSpace(namespace) return space.hasPerm(caller) From 159578584696ce79fbab5f8d81295e7c56919077 Mon Sep 17 00:00:00 2001 From: anarcher Date: Mon, 16 Jan 2023 23:37:38 +0900 Subject: [PATCH 15/20] chore: remove todo comment --- examples/gno.land/r/system/names/public.gno | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index 5cddb736fb4..e41305473ce 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -34,7 +34,6 @@ func AddAdmin(namespace string, newAdmin std.Address) { func RemoveAdmin(namespace string, admin std.Address) { space := getSpace(namespace) assertIsAdmin(space) - // TODO: prevent self err := space.removeAdmin(admin) checkErr(err) } From 904bd0be8e6dd4da08316321cceeb7f93e838540 Mon Sep 17 00:00:00 2001 From: anarcher Date: Thu, 19 Jan 2023 23:35:07 +0900 Subject: [PATCH 16/20] fix: avl.iterate --- examples/gno.land/r/system/names/public.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index e41305473ce..d3530aff89a 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -107,7 +107,7 @@ func renderNamespace(namespace string) string { func renderIndex() string { output := "## Namespaces \n" - namespaces.Iterate("", "", func(n *avl.Tree) bool { + namespaces.Iterate("", "", func(n *avl.Node) bool { namespace := n.Key() space := n.Value().(*Space) output += ufmt.Sprintf("* [%s](/r/system/names:n/%s) - admins: %d editors: %d inPause: %s \n", From 715e72fef794a1f0bd3e038a3a397859980081e4 Mon Sep 17 00:00:00 2001 From: anarcher Date: Thu, 8 Jun 2023 01:36:30 +0900 Subject: [PATCH 17/20] chore: check enabled --- examples/gno.land/r/system/names/public.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index d3530aff89a..55a21adad7b 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -64,7 +64,7 @@ func SetInPause(namespace string, state bool) { // The vm keeper will use this function to check to add package func HasPerm(namespace string) bool { // if enabled is false, it always returns true for dev and testing. - if enabled == false { + if !enabled { return true } caller := std.GetOrigCaller() From d66c61591f4f40e6ebd7211cc8d6021f4248ff4b Mon Sep 17 00:00:00 2001 From: anarcher Date: Fri, 9 Jun 2023 02:27:33 +0900 Subject: [PATCH 18/20] fix: avl.Tree.Iterate --- examples/gno.land/r/system/names/public.gno | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index 55a21adad7b..ccf5c00e0dd 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -3,7 +3,6 @@ package names import ( "std" - "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" ) @@ -107,9 +106,8 @@ func renderNamespace(namespace string) string { func renderIndex() string { output := "## Namespaces \n" - namespaces.Iterate("", "", func(n *avl.Node) bool { - namespace := n.Key() - space := n.Value().(*Space) + namespaces.Iterate("", "", func(namespace string, value interface{}) bool { + space := value.(*Space) output += ufmt.Sprintf("* [%s](/r/system/names:n/%s) - admins: %d editors: %d inPause: %s \n", namespace, namespace, len(space.Admins), len(space.Editors), formatBool(space.InPause)) return false From 6852b1788456ba4400f921abdd818789aa43cf7a Mon Sep 17 00:00:00 2001 From: anarcher Date: Fri, 9 Jun 2023 02:47:17 +0900 Subject: [PATCH 19/20] chore: make fmt --- examples/gno.land/r/system/names/public.gno | 1 - examples/gno.land/r/system/names/public_test.gno | 1 - tm2/pkg/sdk/vm/keeper.go | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/gno.land/r/system/names/public.gno b/examples/gno.land/r/system/names/public.gno index ccf5c00e0dd..8c82f903fce 100644 --- a/examples/gno.land/r/system/names/public.gno +++ b/examples/gno.land/r/system/names/public.gno @@ -101,7 +101,6 @@ func renderNamespace(namespace string) string { `, namespace, renderAddresses(space.Admins), renderAddresses(space.Editors), formatBool(space.InPause)) return output - } func renderIndex() string { diff --git a/examples/gno.land/r/system/names/public_test.gno b/examples/gno.land/r/system/names/public_test.gno index 0d8de50519a..dc840024d2d 100644 --- a/examples/gno.land/r/system/names/public_test.gno +++ b/examples/gno.land/r/system/names/public_test.gno @@ -59,7 +59,6 @@ func TestSpaceMethods(t *testing.T) { s.addAdmin(test1) assertTrue(t, s.removeAdmin(test1) == nil) - } func assertTrue(t *testing.T, b bool) { diff --git a/tm2/pkg/sdk/vm/keeper.go b/tm2/pkg/sdk/vm/keeper.go index a251effd8ca..f26dafe6134 100644 --- a/tm2/pkg/sdk/vm/keeper.go +++ b/tm2/pkg/sdk/vm/keeper.go @@ -150,7 +150,7 @@ func (vm *VMKeeper) checkNamespacePerm(ctx sdk.Context, creator crypto.Address, if err != nil { return err } - //TODO: needs fixed representation of bool + // TODO: needs fixed representation of bool if res != "(true bool)" { return fmt.Errorf("namespace %q not allowed", namespace) } From a61c9df2c182beacc7b4d289ca1927fca20b5319 Mon Sep 17 00:00:00 2001 From: anarcher Date: Sat, 22 Jul 2023 07:48:17 +0900 Subject: [PATCH 20/20] chore: update gno.mod --- examples/gno.land/r/system/names/gno.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/system/names/gno.mod b/examples/gno.land/r/system/names/gno.mod index 31c456f90e0..3a19b02113a 100644 --- a/examples/gno.land/r/system/names/gno.mod +++ b/examples/gno.land/r/system/names/gno.mod @@ -2,4 +2,5 @@ module gno.land/r/system/names require ( "gno.land/p/demo/avl" v0.0.0-latest + "gno.land/p/demo/ufmt" v0.0.0-latest )