From 81329b78119696c760fe7bad25c20e2fc1297b51 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 17:42:03 +0900 Subject: [PATCH 01/26] feat: add p/demo/administrable Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/administrable.gno | 78 +++++++++++++++++++ .../demo/administrable/administrable_test.gno | 8 ++ .../gno.land/p/demo/administrable/doc.gno | 3 + 3 files changed, 89 insertions(+) create mode 100644 examples/gno.land/p/demo/administrable/administrable.gno create mode 100644 examples/gno.land/p/demo/administrable/administrable_test.gno create mode 100644 examples/gno.land/p/demo/administrable/doc.gno diff --git a/examples/gno.land/p/demo/administrable/administrable.gno b/examples/gno.land/p/demo/administrable/administrable.gno new file mode 100644 index 00000000000..f52fdfbdd69 --- /dev/null +++ b/examples/gno.land/p/demo/administrable/administrable.gno @@ -0,0 +1,78 @@ +package administrable + +import ( + "fmt" + "std" +) + + +func New() *Administrable { + return NewWithAddress(std.GetOrigCaller()) +} + +func NewWithAddress(addr std.Address) *Administrable { + a := Administrable{ + addrs: make(map[std.Address]struct{}), + } + a.add(addr) + return &a +} + +type Administrable struct { + addrs map[std.Address]struct{} +} + +func (a *Administrable) List() []std.Address{ + list := make([]std.Address, len(a.addrs)) + idx := 0 + for addr := range a.addrs { + list[idx] = addr + idx++ + } + return list +} + +func (a *Administrable) add(addr std.Address) { + // XXX: should we panic if addr was already present? + a.addrs[addr] = struct{}{} +} + +func (a *Administrable) del(addr std.Address) { + // XXX: should we prevent deleting self? + if len(a.addrs) == 0 { + panic("cannot have no admin.") + } + delete(a.addrs, addr) +} + +func (a *Administrable) Has(addr std.Address) bool { + _, found := a.addrs[addr] + return found +} + +func (a *Administrable) IsAdmin() bool { + if a.Has(std.GetOrigCaller()){ + return true + } + + // XXX: also check for std.PrevRealm, when merged. + + return false +} + +// AssertIsAdmin checks wether the std.GetOrigCaller or std.PrevRealm is whitelisted. +func (a *Administrable) AssertIsAdmin() { + if !a.IsAdmin() { + panic("restricted area.") + } +} + +func (a *Administrable) Add(addr std.Address) { + a.AssertIsAdmin() + a.add(addr) +} + +func (a *Administrable) Del(addr std.Address) { + a.AssertIsAdmin() + a.del(addr) +} diff --git a/examples/gno.land/p/demo/administrable/administrable_test.gno b/examples/gno.land/p/demo/administrable/administrable_test.gno new file mode 100644 index 00000000000..86adb2c986d --- /dev/null +++ b/examples/gno.land/p/demo/administrable/administrable_test.gno @@ -0,0 +1,8 @@ +package administrable + +import "testing" + +func TestPackage(t *testing.T){ + var admin = New() + println(admin.List()) +} diff --git a/examples/gno.land/p/demo/administrable/doc.gno b/examples/gno.land/p/demo/administrable/doc.gno new file mode 100644 index 00000000000..3246f3850b0 --- /dev/null +++ b/examples/gno.land/p/demo/administrable/doc.gno @@ -0,0 +1,3 @@ +// Administrable enables you to add a special account to your contract, granting exclusive privileges for certain actions. +// Its key features include easy admin initialization, administrator management, support for `std.OrigCaller` and `std.PrevRealm`, and a compact API. +package administrable // import "gno.land/p/demo/administrable" From 6dd6abbc685e57010dd437f2703f75d59d560fd8 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:22:22 +0900 Subject: [PATCH 02/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/administrable.gno | 46 ++++++++++++------- .../demo/administrable/administrable_test.gno | 11 ++++- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/examples/gno.land/p/demo/administrable/administrable.gno b/examples/gno.land/p/demo/administrable/administrable.gno index f52fdfbdd69..e97007146b2 100644 --- a/examples/gno.land/p/demo/administrable/administrable.gno +++ b/examples/gno.land/p/demo/administrable/administrable.gno @@ -5,11 +5,13 @@ import ( "std" ) - +// New returns an Administrable object initialized with the caller as the unique admin. +// It is recommended to use this function in the `init()` function of the calling realm. func New() *Administrable { return NewWithAddress(std.GetOrigCaller()) } +// NewWithAddress returns an Administrable object initialized with the provided address as the admin. func NewWithAddress(addr std.Address) *Administrable { a := Administrable{ addrs: make(map[std.Address]struct{}), @@ -18,11 +20,14 @@ func NewWithAddress(addr std.Address) *Administrable { return &a } +// Administrable is an object containing the configuration and allowing to apply filters. +// It's suited to be used as a contract-side global variable or can be embedded in another go object. type Administrable struct { addrs map[std.Address]struct{} } -func (a *Administrable) List() []std.Address{ +// List returns a slice containing the addresses of all administrators. +func (a *Administrable) List() []std.Address { list := make([]std.Address, len(a.addrs)) idx := 0 for addr := range a.addrs { @@ -32,26 +37,15 @@ func (a *Administrable) List() []std.Address{ return list } -func (a *Administrable) add(addr std.Address) { - // XXX: should we panic if addr was already present? - a.addrs[addr] = struct{}{} -} - -func (a *Administrable) del(addr std.Address) { - // XXX: should we prevent deleting self? - if len(a.addrs) == 0 { - panic("cannot have no admin.") - } - delete(a.addrs, addr) -} - +// Has checks if an address is in the list of administrators. func (a *Administrable) Has(addr std.Address) bool { _, found := a.addrs[addr] return found } +// IsAdmin checks if the caller or prevRealm is an administrator. func (a *Administrable) IsAdmin() bool { - if a.Has(std.GetOrigCaller()){ + if a.Has(std.GetOrigCaller()) { return true } @@ -60,19 +54,37 @@ func (a *Administrable) IsAdmin() bool { return false } -// AssertIsAdmin checks wether the std.GetOrigCaller or std.PrevRealm is whitelisted. +// AssertIsAdmin checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. +// If not, it panics indicating restricted access. func (a *Administrable) AssertIsAdmin() { if !a.IsAdmin() { panic("restricted area.") } } +// Add adds an address to the list of administrators. +// It requires the caller or prevRealm to be an admin, otherwise, it panics. func (a *Administrable) Add(addr std.Address) { a.AssertIsAdmin() a.add(addr) } +// Del removes an address from the list of administrators. +// It requires the caller or prevRealm to be an admin, otherwise, it panics. func (a *Administrable) Del(addr std.Address) { a.AssertIsAdmin() a.del(addr) } + +func (a *Administrable) add(addr std.Address) { + // XXX: should we panic if addr was already present? + a.addrs[addr] = struct{}{} +} + +func (a *Administrable) del(addr std.Address) { + // XXX: should we prevent deleting self? + if len(a.addrs) == 0 { + panic("cannot have no admin.") + } + delete(a.addrs, addr) +} diff --git a/examples/gno.land/p/demo/administrable/administrable_test.gno b/examples/gno.land/p/demo/administrable/administrable_test.gno index 86adb2c986d..ae0e5e5fb49 100644 --- a/examples/gno.land/p/demo/administrable/administrable_test.gno +++ b/examples/gno.land/p/demo/administrable/administrable_test.gno @@ -2,7 +2,14 @@ package administrable import "testing" -func TestPackage(t *testing.T){ - var admin = New() +func TestPackage(t *testing.T) { + admin := New() println(admin.List()) } + +func ExamplePackageLevel() { +} + +func ExampleEmbedding() { + println("test") +} From 45288577596db57bafb90e779cf36adc3f39a947 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:39:42 +0900 Subject: [PATCH 03/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/administrable.gno | 32 ++--- .../demo/administrable/administrable_test.gno | 125 +++++++++++++++++- 2 files changed, 136 insertions(+), 21 deletions(-) diff --git a/examples/gno.land/p/demo/administrable/administrable.gno b/examples/gno.land/p/demo/administrable/administrable.gno index e97007146b2..c11499d20de 100644 --- a/examples/gno.land/p/demo/administrable/administrable.gno +++ b/examples/gno.land/p/demo/administrable/administrable.gno @@ -5,29 +5,29 @@ import ( "std" ) -// New returns an Administrable object initialized with the caller as the unique admin. +// New returns a Set object initialized with the caller as the unique admin. // It is recommended to use this function in the `init()` function of the calling realm. -func New() *Administrable { +func New() Set { return NewWithAddress(std.GetOrigCaller()) } -// NewWithAddress returns an Administrable object initialized with the provided address as the admin. -func NewWithAddress(addr std.Address) *Administrable { - a := Administrable{ +// NewWithAddress returns a Set object initialized with the provided address as the admin. +func NewWithAddress(addr std.Address) Set { + a := Set{ addrs: make(map[std.Address]struct{}), } a.add(addr) - return &a + return a } -// Administrable is an object containing the configuration and allowing to apply filters. +// Set is an object containing the configuration and allowing to apply filters. // It's suited to be used as a contract-side global variable or can be embedded in another go object. -type Administrable struct { +type Set struct { addrs map[std.Address]struct{} } // List returns a slice containing the addresses of all administrators. -func (a *Administrable) List() []std.Address { +func (a *Set) List() []std.Address { list := make([]std.Address, len(a.addrs)) idx := 0 for addr := range a.addrs { @@ -38,13 +38,13 @@ func (a *Administrable) List() []std.Address { } // Has checks if an address is in the list of administrators. -func (a *Administrable) Has(addr std.Address) bool { +func (a *Set) Has(addr std.Address) bool { _, found := a.addrs[addr] return found } // IsAdmin checks if the caller or prevRealm is an administrator. -func (a *Administrable) IsAdmin() bool { +func (a *Set) IsAdmin() bool { if a.Has(std.GetOrigCaller()) { return true } @@ -56,7 +56,7 @@ func (a *Administrable) IsAdmin() bool { // AssertIsAdmin checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. // If not, it panics indicating restricted access. -func (a *Administrable) AssertIsAdmin() { +func (a *Set) AssertIsAdmin() { if !a.IsAdmin() { panic("restricted area.") } @@ -64,24 +64,24 @@ func (a *Administrable) AssertIsAdmin() { // Add adds an address to the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. -func (a *Administrable) Add(addr std.Address) { +func (a *Set) Add(addr std.Address) { a.AssertIsAdmin() a.add(addr) } // Del removes an address from the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. -func (a *Administrable) Del(addr std.Address) { +func (a *Set) Del(addr std.Address) { a.AssertIsAdmin() a.del(addr) } -func (a *Administrable) add(addr std.Address) { +func (a *Set) add(addr std.Address) { // XXX: should we panic if addr was already present? a.addrs[addr] = struct{}{} } -func (a *Administrable) del(addr std.Address) { +func (a *Set) del(addr std.Address) { // XXX: should we prevent deleting self? if len(a.addrs) == 0 { panic("cannot have no admin.") diff --git a/examples/gno.land/p/demo/administrable/administrable_test.gno b/examples/gno.land/p/demo/administrable/administrable_test.gno index ae0e5e5fb49..df33129dc86 100644 --- a/examples/gno.land/p/demo/administrable/administrable_test.gno +++ b/examples/gno.land/p/demo/administrable/administrable_test.gno @@ -1,15 +1,130 @@ package administrable -import "testing" +import ( + "std" + "testing" -func TestPackage(t *testing.T) { - admin := New() - println(admin.List()) + "gno.land/p/demo/testutils" +) + +func TestSet_AddAndRemoveAdmin(t *testing.T) { + set := New() + + // Add an admin + admin1 := testutils.TestAddress("test1") + set.Add(admin1) + if !set.Has(admin1) { + t.Errorf("Expected admin1 to be added, but it was not") + } + + // Remove the admin + set.Del(admin1) + if set.Has(admin1) { + t.Errorf("Expected admin1 to be removed, but it was still present") + } +} + +func TestSet_ListAdmins(t *testing.T) { + // Add multiple admins + admin1 := testutils.TestAddress("test2") + admin2 := testutils.TestAddress("test3") + set := NewWithAddress(admin1) + std.TestSetOrigCaller(admin1) + set.Add(admin2) + + // Get the list of admins + admins := set.List() + + // Verify the correct number of admins + expectedAdminsCount := 2 + if len(admins) != expectedAdminsCount { + t.Errorf("Expected %d admins, but got %d", expectedAdminsCount, len(admins)) + } + + // Verify the admins in the list + expectedAdmins := []std.Address{admin1, admin2} + for _, expectedAdmin := range expectedAdmins { + found := false + for _, admin := range admins { + if admin == expectedAdmin { + found = true + break + } + } + if !found { + t.Errorf("Expected admin %s to be in the list, but it was not found", expectedAdmin) + } + } +} + +func TestSet_IsAdmin(t *testing.T) { + // Set the original caller as admin + admin := testutils.TestAddress("test4") + std.TestSetOrigCaller(admin) + set := New() + + // Verify IsAdmin returns true for the admin + if !set.IsAdmin() { + t.Errorf("Expected IsAdmin to return true for the admin, but it returned false") + } + + // Verify IsAdmin returns false for a non-admin + nonAdmin := testutils.TestAddress("test5") + std.TestSetOrigCaller(nonAdmin) + if set.IsAdmin() { + t.Errorf("Expected IsAdmin to return false for a non-admin, but it returned true") + } +} + +func TestSet_AddAndRemoveAdmin_Panic(t *testing.T) { + + // Add an admin + admin := testutils.TestAddress("test6") + std.TestSetOrigCaller(admin) + set := New() + + // Set a non-admin caller + nonAdmin := testutils.TestAddress("test7") + std.TestSetOrigCaller(nonAdmin) + + // Verify Add panics when called by a non-admin + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected Add to panic when called by a non-admin, but it did not panic") + } + }() + set.Add(admin) + + // Verify Del panics when called by a non-admin + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected Del to panic when called by a non-admin, but it did not panic") + } + }() + set.Del(admin) } func ExamplePackageLevel() { + // set this globally or during init() + var acl = New() + + // in functions + acl.AssertIsAdmin() } func ExampleEmbedding() { - println("test") + // declare a new struct, and embed administrable.Set. + type MyObject struct{ + myField int + Set + } + + // initialize the object, it now has its own admin set. + myObject := MyObject{ + myField: 42, + Set: New(), + } + + // check from the context of the object. + myObject.Set.AssertIsAdmin() } From 6163c9b1b461e01cd82e19095ee628d78ad75342 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 19:41:43 +0900 Subject: [PATCH 04/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/administrable.gno | 88 +++++++++++-------- .../demo/administrable/administrable_test.gno | 7 +- .../p/demo/administrable/z0_filetest.gno | 44 ++++++++++ 3 files changed, 96 insertions(+), 43 deletions(-) create mode 100644 examples/gno.land/p/demo/administrable/z0_filetest.gno diff --git a/examples/gno.land/p/demo/administrable/administrable.gno b/examples/gno.land/p/demo/administrable/administrable.gno index c11499d20de..c6d50993062 100644 --- a/examples/gno.land/p/demo/administrable/administrable.gno +++ b/examples/gno.land/p/demo/administrable/administrable.gno @@ -5,47 +5,45 @@ import ( "std" ) -// New returns a Set object initialized with the caller as the unique admin. +// New returns s Set object initialized with the caller as the unique admin. // It is recommended to use this function in the `init()` function of the calling realm. func New() Set { - return NewWithAddress(std.GetOrigCaller()) + addr := std.GetOrigCaller() + return NewWithAddress(addr) } -// NewWithAddress returns a Set object initialized with the provided address as the admin. +// NewWithAddress returns s Set object initialized with the provided address as the admin. func NewWithAddress(addr std.Address) Set { - a := Set{ - addrs: make(map[std.Address]struct{}), + return Set{ + addrs: []std.Address{addr}, } - a.add(addr) - return a } // Set is an object containing the configuration and allowing to apply filters. // It's suited to be used as a contract-side global variable or can be embedded in another go object. type Set struct { - addrs map[std.Address]struct{} + // XXX: replace with map[std.Address]struct{} + addrs []std.Address } // List returns a slice containing the addresses of all administrators. -func (a *Set) List() []std.Address { - list := make([]std.Address, len(a.addrs)) - idx := 0 - for addr := range a.addrs { - list[idx] = addr - idx++ - } - return list +func (s *Set) List() []std.Address { + return s.addrs } // Has checks if an address is in the list of administrators. -func (a *Set) Has(addr std.Address) bool { - _, found := a.addrs[addr] - return found +func (s *Set) Has(addr std.Address) bool { + for _, entry := range s.addrs { + if entry == addr { + return true + } + } + return false } // IsAdmin checks if the caller or prevRealm is an administrator. -func (a *Set) IsAdmin() bool { - if a.Has(std.GetOrigCaller()) { +func (s *Set) IsAdmin() bool { + if s.Has(std.GetOrigCaller()) { return true } @@ -56,35 +54,47 @@ func (a *Set) IsAdmin() bool { // AssertIsAdmin checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. // If not, it panics indicating restricted access. -func (a *Set) AssertIsAdmin() { - if !a.IsAdmin() { - panic("restricted area.") +func (s *Set) AssertIsAdmin() { + if !s.IsAdmin() { + panic("restricted ares.") } } // Add adds an address to the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. -func (a *Set) Add(addr std.Address) { - a.AssertIsAdmin() - a.add(addr) +func (s *Set) Add(addr std.Address) { + s.AssertIsAdmin() + if s.Has(addr) { + // XXX: panic? + return + } + s.addrs = append(s.addrs, addr) } // Del removes an address from the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. -func (a *Set) Del(addr std.Address) { - a.AssertIsAdmin() - a.del(addr) -} - -func (a *Set) add(addr std.Address) { - // XXX: should we panic if addr was already present? - a.addrs[addr] = struct{}{} -} +func (s *Set) Del(addr std.Address) (success bool) { + s.AssertIsAdmin() -func (a *Set) del(addr std.Address) { // XXX: should we prevent deleting self? - if len(a.addrs) == 0 { + + // prevent deleting the last admin. + if len(s.addrs) == 1 { panic("cannot have no admin.") } - delete(a.addrs, addr) + + for i, entry := range s.addrs { + if entry == addr { + s.addrs = append(s.addrs[:i], s.addrs[i+1:]...) + return true + } + } + return false +} + +// ReplaceAll removes all existing admins and replaces them with a new one. +// It requires the caller or prevRealm to be an admin, otherwise, it panics. +func (s *Set) ReplaceAll(addr std.Address) { + s.AssertIsAdmin() + s.addrs = []std.Address{addr} } diff --git a/examples/gno.land/p/demo/administrable/administrable_test.gno b/examples/gno.land/p/demo/administrable/administrable_test.gno index df33129dc86..e62c5901a48 100644 --- a/examples/gno.land/p/demo/administrable/administrable_test.gno +++ b/examples/gno.land/p/demo/administrable/administrable_test.gno @@ -77,7 +77,6 @@ func TestSet_IsAdmin(t *testing.T) { } func TestSet_AddAndRemoveAdmin_Panic(t *testing.T) { - // Add an admin admin := testutils.TestAddress("test6") std.TestSetOrigCaller(admin) @@ -106,7 +105,7 @@ func TestSet_AddAndRemoveAdmin_Panic(t *testing.T) { func ExamplePackageLevel() { // set this globally or during init() - var acl = New() + acl := New() // in functions acl.AssertIsAdmin() @@ -114,7 +113,7 @@ func ExamplePackageLevel() { func ExampleEmbedding() { // declare a new struct, and embed administrable.Set. - type MyObject struct{ + type MyObject struct { myField int Set } @@ -122,7 +121,7 @@ func ExampleEmbedding() { // initialize the object, it now has its own admin set. myObject := MyObject{ myField: 42, - Set: New(), + Set: New(), } // check from the context of the object. diff --git a/examples/gno.land/p/demo/administrable/z0_filetest.gno b/examples/gno.land/p/demo/administrable/z0_filetest.gno new file mode 100644 index 00000000000..aed2f0d63e1 --- /dev/null +++ b/examples/gno.land/p/demo/administrable/z0_filetest.gno @@ -0,0 +1,44 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" + + "gno.land/p/demo/administrable" + "gno.land/p/demo/testutils" +) + +var admins administrable.Set + +func init() { + admins = administrable.New() +} + +func main() { + initialCaller := std.GetOrigCaller() + test1 := testutils.TestAddress("test1") + test2 := testutils.TestAddress("test2") + println(admins.List()) + admins.Add(test1) + println(admins.List()) + admins.Add(test1) + println(admins.List()) + println(admins.IsAdmin()) + admins.Del(initialCaller) + println(admins.List()) + println(admins.IsAdmin()) + std.TestSetOrigCaller(test2) + println(admins.IsAdmin()) + std.TestSetOrigCaller(test1) + println(admins.IsAdmin()) +} + +// Output: +// slice[ref(a8ada09dee16d791fd406d629fe29bb0ed084a30:5)] +// slice[("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// slice[("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// true +// slice[("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// false +// false +// true From ac38d9c5ee32f7a39df97e29761f93736cf60bc7 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 20:38:13 +0900 Subject: [PATCH 05/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/example_test.gno | 26 +++++ .../{administrable.gno => set.gno} | 32 +++--- .../p/demo/administrable/set_test.gno | 100 ++++++++++++++++++ .../p/demo/administrable/unprivileged.gno | 27 +++++ ...strable_test.gno => unprivileged_test.gno} | 57 +++------- .../p/demo/administrable/z0_filetest.gno | 8 +- examples/gno.land/r/gnoland/blog/admin.gno | 39 ++----- 7 files changed, 199 insertions(+), 90 deletions(-) create mode 100644 examples/gno.land/p/demo/administrable/example_test.gno rename examples/gno.land/p/demo/administrable/{administrable.gno => set.gno} (80%) create mode 100644 examples/gno.land/p/demo/administrable/set_test.gno create mode 100644 examples/gno.land/p/demo/administrable/unprivileged.gno rename examples/gno.land/p/demo/administrable/{administrable_test.gno => unprivileged_test.gno} (64%) diff --git a/examples/gno.land/p/demo/administrable/example_test.gno b/examples/gno.land/p/demo/administrable/example_test.gno new file mode 100644 index 00000000000..adee2303a61 --- /dev/null +++ b/examples/gno.land/p/demo/administrable/example_test.gno @@ -0,0 +1,26 @@ +package administrable + +func ExamplePackageLevel() { + // set this globally or during init() + acl := New() + + // in functions + acl.AssertCurrentHasAccess() +} + +func ExampleEmbedding() { + // declare a new struct, and embed administrable.Set. + type MyObject struct { + myField int + Set + } + + // initialize the object, it now has its own admin set. + myObject := MyObject{ + myField: 42, + Set: New(), + } + + // check from the context of the object. + myObject.Set.AssertCurrentHasAccess() +} diff --git a/examples/gno.land/p/demo/administrable/administrable.gno b/examples/gno.land/p/demo/administrable/set.gno similarity index 80% rename from examples/gno.land/p/demo/administrable/administrable.gno rename to examples/gno.land/p/demo/administrable/set.gno index c6d50993062..59d022ab118 100644 --- a/examples/gno.land/p/demo/administrable/administrable.gno +++ b/examples/gno.land/p/demo/administrable/set.gno @@ -9,20 +9,22 @@ import ( // It is recommended to use this function in the `init()` function of the calling realm. func New() Set { addr := std.GetOrigCaller() - return NewWithAddress(addr) + return Set{ + addrs: []std.Address{addr}, + } } // NewWithAddress returns s Set object initialized with the provided address as the admin. func NewWithAddress(addr std.Address) Set { - return Set{ - addrs: []std.Address{addr}, - } + s := New() + s.ReplaceAll(addr) + return s } // Set is an object containing the configuration and allowing to apply filters. // It's suited to be used as a contract-side global variable or can be embedded in another go object. type Set struct { - // XXX: replace with map[std.Address]struct{} + // XXX: replace with map[std.Address]struct{}? addrs []std.Address } @@ -32,7 +34,7 @@ func (s *Set) List() []std.Address { } // Has checks if an address is in the list of administrators. -func (s *Set) Has(addr std.Address) bool { +func (s *Set) HasAccess(addr std.Address) bool { for _, entry := range s.addrs { if entry == addr { return true @@ -41,9 +43,9 @@ func (s *Set) Has(addr std.Address) bool { return false } -// IsAdmin checks if the caller or prevRealm is an administrator. -func (s *Set) IsAdmin() bool { - if s.Has(std.GetOrigCaller()) { +// CurrentHasAccess checks if the caller or prevRealm is an administrator. +func (s *Set) CurrentHasAccess() bool { + if s.HasAccess(std.GetOrigCaller()) { return true } @@ -52,10 +54,10 @@ func (s *Set) IsAdmin() bool { return false } -// AssertIsAdmin checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. +// AssertCurrentHasAccess checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. // If not, it panics indicating restricted access. -func (s *Set) AssertIsAdmin() { - if !s.IsAdmin() { +func (s *Set) AssertCurrentHasAccess() { + if !s.CurrentHasAccess() { panic("restricted ares.") } } @@ -63,8 +65,7 @@ func (s *Set) AssertIsAdmin() { // Add adds an address to the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. func (s *Set) Add(addr std.Address) { - s.AssertIsAdmin() - if s.Has(addr) { + if s.HasAccess(addr) { // XXX: panic? return } @@ -74,8 +75,6 @@ func (s *Set) Add(addr std.Address) { // Del removes an address from the list of administrators. // It requires the caller or prevRealm to be an admin, otherwise, it panics. func (s *Set) Del(addr std.Address) (success bool) { - s.AssertIsAdmin() - // XXX: should we prevent deleting self? // prevent deleting the last admin. @@ -95,6 +94,5 @@ func (s *Set) Del(addr std.Address) (success bool) { // ReplaceAll removes all existing admins and replaces them with a new one. // It requires the caller or prevRealm to be an admin, otherwise, it panics. func (s *Set) ReplaceAll(addr std.Address) { - s.AssertIsAdmin() s.addrs = []std.Address{addr} } diff --git a/examples/gno.land/p/demo/administrable/set_test.gno b/examples/gno.land/p/demo/administrable/set_test.gno new file mode 100644 index 00000000000..9b3801d71ee --- /dev/null +++ b/examples/gno.land/p/demo/administrable/set_test.gno @@ -0,0 +1,100 @@ +package administrable + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" +) + +func Test(t *testing.T) { + println("TYOOOFSOIJFf") +} + +/* +func TestSet_AddAndRemoveAdmin(t *testing.T) { + set := New() + + // Add an admin + admin1 := testutils.TestAddress("test1") + set.Add(admin1) + if !set.HasAccess(admin1) { + t.Errorf("Expected admin1 to be added, but it was not") + } + + // Remove the admin + set.Del(admin1) + if set.HasAccess(admin1) { + t.Errorf("Expected admin1 to be removed, but it was still present") + } +} + +func TestSet_ListAdmins(t *testing.T) { + // Add multiple admins + admin1 := testutils.TestAddress("test2") + admin2 := testutils.TestAddress("test3") + set := NewWithAddress(admin1) + std.TestSetOrigCaller(admin1) + set.Add(admin2) + + // Get the list of admins + admins := set.List() + + // Verify the correct number of admins + expectedAdminsCount := 2 + if len(admins) != expectedAdminsCount { + t.Errorf("Expected %d admins, but got %d", expectedAdminsCount, len(admins)) + } + + // Verify the admins in the list + expectedAdmins := []std.Address{admin1, admin2} + for _, expectedAdmin := range expectedAdmins { + found := false + for _, admin := range admins { + if admin == expectedAdmin { + found = true + break + } + } + if !found { + t.Errorf("Expected admin %s to be in the list, but it was not found", expectedAdmin) + } + } +} + +func TestSet_CurrentHasAccess(t *testing.T) { + // Set the original caller as admin + admin := testutils.TestAddress("test4") + std.TestSetOrigCaller(admin) + set := New() + + // Verify CurrentHasAccess returns true for the admin + if !set.CurrentHasAccess() { + t.Errorf("Expected CurrentHasAccess to return true for the admin, but it returned false") + } + + // Verify CurrentHasAccess returns false for a non-admin + nonAdmin := testutils.TestAddress("test5") + std.TestSetOrigCaller(nonAdmin) + if set.CurrentHasAccess() { + t.Errorf("Expected CurrentHasAccess to return false for a non-admin, but it returned true") + } +} + +func TestSet_AddAndRemoveAdmin_WontPanic(t *testing.T) { + // Add an admin + admin := testutils.TestAddress("test6") + std.TestSetOrigCaller(admin) + set := New() + + // Set a non-admin caller + nonAdmin := testutils.TestAddress("test7") + std.TestSetOrigCaller(nonAdmin) + + // Verify Add won't panic in Privileged mode + set.Add(nonAdmin) + + // Verify Del won't panic in Privileged mode + set.Del(nonAdmin) +} +*/ diff --git a/examples/gno.land/p/demo/administrable/unprivileged.gno b/examples/gno.land/p/demo/administrable/unprivileged.gno new file mode 100644 index 00000000000..68864a31cf7 --- /dev/null +++ b/examples/gno.land/p/demo/administrable/unprivileged.gno @@ -0,0 +1,27 @@ +package administrable + +import "std" + +type UnprivilegedSet struct { + Set +} + +// Unprivileged returns an unprivileged structure that can be exposed safely. +func (s *Set) Unprivileged() UnprivilegedSet { + return UnprivilegedSet{Set: *s} +} + +func (u *UnprivilegedSet) Add(addr std.Address) { + u.AssertCurrentHasAccess() + u.Set.Add(addr) +} + +func (u *UnprivilegedSet) Del(addr std.Address) { + u.AssertCurrentHasAccess() + u.Set.Del(addr) +} + +func (u *UnprivilegedSet) ReplaceAll(addr std.Address) { + u.AssertCurrentHasAccess() + u.Set.ReplaceAll(addr) +} diff --git a/examples/gno.land/p/demo/administrable/administrable_test.gno b/examples/gno.land/p/demo/administrable/unprivileged_test.gno similarity index 64% rename from examples/gno.land/p/demo/administrable/administrable_test.gno rename to examples/gno.land/p/demo/administrable/unprivileged_test.gno index e62c5901a48..6f97b4b9ee3 100644 --- a/examples/gno.land/p/demo/administrable/administrable_test.gno +++ b/examples/gno.land/p/demo/administrable/unprivileged_test.gno @@ -7,28 +7,28 @@ import ( "gno.land/p/demo/testutils" ) -func TestSet_AddAndRemoveAdmin(t *testing.T) { - set := New() +func TestUnprivilegedSet_AddAndRemoveAdmin(t *testing.T) { + set := New().Unprivileged() // Add an admin admin1 := testutils.TestAddress("test1") set.Add(admin1) - if !set.Has(admin1) { + if !set.HasAccess(admin1) { t.Errorf("Expected admin1 to be added, but it was not") } // Remove the admin set.Del(admin1) - if set.Has(admin1) { + if set.HasAccess(admin1) { t.Errorf("Expected admin1 to be removed, but it was still present") } } -func TestSet_ListAdmins(t *testing.T) { +func TestUnprivilegedSet_ListAdmins(t *testing.T) { // Add multiple admins admin1 := testutils.TestAddress("test2") admin2 := testutils.TestAddress("test3") - set := NewWithAddress(admin1) + set := NewWithAddress(admin1).Unprivileged() std.TestSetOrigCaller(admin1) set.Add(admin2) @@ -57,30 +57,30 @@ func TestSet_ListAdmins(t *testing.T) { } } -func TestSet_IsAdmin(t *testing.T) { +func TestUnprivilegedSet_CurrentHasAccess(t *testing.T) { // Set the original caller as admin admin := testutils.TestAddress("test4") std.TestSetOrigCaller(admin) - set := New() + set := New().Unprivileged() - // Verify IsAdmin returns true for the admin - if !set.IsAdmin() { - t.Errorf("Expected IsAdmin to return true for the admin, but it returned false") + // Verify CurrentHasAccess returns true for the admin + if !set.CurrentHasAccess() { + t.Errorf("Expected CurrentHasAccess to return true for the admin, but it returned false") } - // Verify IsAdmin returns false for a non-admin + // Verify CurrentHasAccess returns false for a non-admin nonAdmin := testutils.TestAddress("test5") std.TestSetOrigCaller(nonAdmin) - if set.IsAdmin() { - t.Errorf("Expected IsAdmin to return false for a non-admin, but it returned true") + if set.CurrentHasAccess() { + t.Errorf("Expected CurrentHasAccess to return false for a non-admin, but it returned true") } } -func TestSet_AddAndRemoveAdmin_Panic(t *testing.T) { +func TestUnprivilegedSet_AddAndRemoveAdmin_Panic(t *testing.T) { // Add an admin admin := testutils.TestAddress("test6") std.TestSetOrigCaller(admin) - set := New() + set := New().Unprivileged() // Set a non-admin caller nonAdmin := testutils.TestAddress("test7") @@ -102,28 +102,3 @@ func TestSet_AddAndRemoveAdmin_Panic(t *testing.T) { }() set.Del(admin) } - -func ExamplePackageLevel() { - // set this globally or during init() - acl := New() - - // in functions - acl.AssertIsAdmin() -} - -func ExampleEmbedding() { - // declare a new struct, and embed administrable.Set. - type MyObject struct { - myField int - Set - } - - // initialize the object, it now has its own admin set. - myObject := MyObject{ - myField: 42, - Set: New(), - } - - // check from the context of the object. - myObject.Set.AssertIsAdmin() -} diff --git a/examples/gno.land/p/demo/administrable/z0_filetest.gno b/examples/gno.land/p/demo/administrable/z0_filetest.gno index aed2f0d63e1..7082137cd34 100644 --- a/examples/gno.land/p/demo/administrable/z0_filetest.gno +++ b/examples/gno.land/p/demo/administrable/z0_filetest.gno @@ -23,14 +23,14 @@ func main() { println(admins.List()) admins.Add(test1) println(admins.List()) - println(admins.IsAdmin()) + println(admins.CurrentHasAccess()) admins.Del(initialCaller) println(admins.List()) - println(admins.IsAdmin()) + println(admins.CurrentHasAccess()) std.TestSetOrigCaller(test2) - println(admins.IsAdmin()) + println(admins.CurrentHasAccess()) std.TestSetOrigCaller(test1) - println(admins.IsAdmin()) + println(admins.CurrentHasAccess()) } // Output: diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 14ac6373552..6fdb78ce061 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -4,19 +4,20 @@ import ( "std" "strings" + "gno.land/p/demo/administrable" "gno.land/p/demo/avl" ) var ( - adminAddr std.Address - moderatorList avl.Tree - commenterList avl.Tree - inPause bool + admins administrable.Set + moderators administrable.Set + commenters administrable.Set + inPause bool ) func init() { - // adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis. - adminAddr = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" + admins = administrable.New() + admins.Add("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @manfred } func AdminSetAdminAddr(addr std.Address) { @@ -25,6 +26,7 @@ func AdminSetAdminAddr(addr std.Address) { } func AdminSetInPause(state bool) { + assertIsAdmin() inPause = state } @@ -72,38 +74,19 @@ func ModDelComment(slug string, index int) { checkErr(err) } -func isAdmin(addr std.Address) bool { - return addr == adminAddr -} - -func isModerator(addr std.Address) bool { - _, found := moderatorList.Get(addr.String()) - return found -} - -func isCommenter(addr std.Address) bool { - _, found := commenterList.Get(addr.String()) - return found -} - func assertIsAdmin() { - caller := std.GetOrigCaller() - if !isAdmin(caller) { - panic("access restricted.") - } + admins.AssertHasAccess() } func assertIsModerator() { - caller := std.GetOrigCaller() - if isAdmin(caller) || isModerator(caller) { + if admins.HasAccess() || moderators.HasAccess() { return } panic("access restricted") } func assertIsCommenter() { - caller := std.GetOrigCaller() - if isAdmin(caller) || isModerator(caller) || isCommenter(caller) { + if admins.HasAccess() || moderators.HasAccess() || commenters.HasAccess() { return } panic("access restricted") From 3bb7b65018fcbd5bc8a7d7edb3e92cb327c8f00c Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 20:47:02 +0900 Subject: [PATCH 06/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/administrable/set.gno | 8 ++++---- examples/gno.land/p/demo/administrable/set_test.gno | 12 +++--------- .../gno.land/p/demo/administrable/unprivileged.gno | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/examples/gno.land/p/demo/administrable/set.gno b/examples/gno.land/p/demo/administrable/set.gno index 59d022ab118..7ee0c24e222 100644 --- a/examples/gno.land/p/demo/administrable/set.gno +++ b/examples/gno.land/p/demo/administrable/set.gno @@ -5,7 +5,7 @@ import ( "std" ) -// New returns s Set object initialized with the caller as the unique admin. +// New returns s Set object initialized with the caller as the unique authorized address. // It is recommended to use this function in the `init()` function of the calling realm. func New() Set { addr := std.GetOrigCaller() @@ -14,7 +14,7 @@ func New() Set { } } -// NewWithAddress returns s Set object initialized with the provided address as the admin. +// NewWithAddress returns s Set object initialized with the provided address as authorized. func NewWithAddress(addr std.Address) Set { s := New() s.ReplaceAll(addr) @@ -28,12 +28,12 @@ type Set struct { addrs []std.Address } -// List returns a slice containing the addresses of all administrators. +// List returns a slice containing all the authorized addresses. func (s *Set) List() []std.Address { return s.addrs } -// Has checks if an address is in the list of administrators. +// HasAccess checks if the provided address is in the list of authorized ones. func (s *Set) HasAccess(addr std.Address) bool { for _, entry := range s.addrs { if entry == addr { diff --git a/examples/gno.land/p/demo/administrable/set_test.gno b/examples/gno.land/p/demo/administrable/set_test.gno index 9b3801d71ee..6dd75a14adf 100644 --- a/examples/gno.land/p/demo/administrable/set_test.gno +++ b/examples/gno.land/p/demo/administrable/set_test.gno @@ -7,11 +7,6 @@ import ( "gno.land/p/demo/testutils" ) -func Test(t *testing.T) { - println("TYOOOFSOIJFf") -} - -/* func TestSet_AddAndRemoveAdmin(t *testing.T) { set := New() @@ -91,10 +86,9 @@ func TestSet_AddAndRemoveAdmin_WontPanic(t *testing.T) { nonAdmin := testutils.TestAddress("test7") std.TestSetOrigCaller(nonAdmin) - // Verify Add won't panic in Privileged mode - set.Add(nonAdmin) + // Verify Add won't panic in Privileged mode + set.Add(nonAdmin) - // Verify Del won't panic in Privileged mode + // Verify Del won't panic in Privileged mode set.Del(nonAdmin) } -*/ diff --git a/examples/gno.land/p/demo/administrable/unprivileged.gno b/examples/gno.land/p/demo/administrable/unprivileged.gno index 68864a31cf7..de37f9cf4a4 100644 --- a/examples/gno.land/p/demo/administrable/unprivileged.gno +++ b/examples/gno.land/p/demo/administrable/unprivileged.gno @@ -7,8 +7,8 @@ type UnprivilegedSet struct { } // Unprivileged returns an unprivileged structure that can be exposed safely. -func (s *Set) Unprivileged() UnprivilegedSet { - return UnprivilegedSet{Set: *s} +func (s Set) Unprivileged() UnprivilegedSet { + return UnprivilegedSet{Set: s} } func (u *UnprivilegedSet) Add(addr std.Address) { From c1b51c0036991eb6ff909eb306cc78bfca348feb Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 20:54:04 +0900 Subject: [PATCH 07/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/administrable/set.gno | 51 ++++++++++--------- .../p/demo/administrable/unprivileged.gno | 5 ++ .../p/demo/administrable/z1_filetest.gno | 40 +++++++++++++++ 3 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 examples/gno.land/p/demo/administrable/z1_filetest.gno diff --git a/examples/gno.land/p/demo/administrable/set.gno b/examples/gno.land/p/demo/administrable/set.gno index 7ee0c24e222..7e1a063045b 100644 --- a/examples/gno.land/p/demo/administrable/set.gno +++ b/examples/gno.land/p/demo/administrable/set.gno @@ -5,7 +5,7 @@ import ( "std" ) -// New returns s Set object initialized with the caller as the unique authorized address. +// New returns a Set object initialized with the caller as the unique authorized address. // It is recommended to use this function in the `init()` function of the calling realm. func New() Set { addr := std.GetOrigCaller() @@ -14,17 +14,16 @@ func New() Set { } } -// NewWithAddress returns s Set object initialized with the provided address as authorized. +// NewWithAddress returns a Set object initialized with the provided address as authorized. func NewWithAddress(addr std.Address) Set { s := New() s.ReplaceAll(addr) return s } -// Set is an object containing the configuration and allowing to apply filters. -// It's suited to be used as a contract-side global variable or can be embedded in another go object. +// Set is an object containing the configuration and allowing the application of filters. +// It is suited to be used as a contract-side global variable or can be embedded in another Go object. type Set struct { - // XXX: replace with map[std.Address]struct{}? addrs []std.Address } @@ -43,7 +42,7 @@ func (s *Set) HasAccess(addr std.Address) bool { return false } -// CurrentHasAccess checks if the caller or prevRealm is an administrator. +// CurrentHasAccess checks if the caller or prevRealm is authorized. func (s *Set) CurrentHasAccess() bool { if s.HasAccess(std.GetOrigCaller()) { return true @@ -54,34 +53,28 @@ func (s *Set) CurrentHasAccess() bool { return false } -// AssertCurrentHasAccess checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as an admin. +// AssertCurrentHasAccess checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as authorized. // If not, it panics indicating restricted access. func (s *Set) AssertCurrentHasAccess() { if !s.CurrentHasAccess() { - panic("restricted ares.") + panic("restricted access.") } } -// Add adds an address to the list of administrators. -// It requires the caller or prevRealm to be an admin, otherwise, it panics. +// Add adds an address to the list of authorized addresses. +// It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Add(addr std.Address) { if s.HasAccess(addr) { - // XXX: panic? + // XXX: Consider panicking or handling duplicate addition differently. return } s.addrs = append(s.addrs, addr) } -// Del removes an address from the list of administrators. -// It requires the caller or prevRealm to be an admin, otherwise, it panics. -func (s *Set) Del(addr std.Address) (success bool) { - // XXX: should we prevent deleting self? - - // prevent deleting the last admin. - if len(s.addrs) == 1 { - panic("cannot have no admin.") - } - +// ForceDel removes an address from the list of authorized addresses. +// It won't panic if there is only one address. +// It requires the caller or prevRealm to be authorized, otherwise, it panics. +func (s *Set) ForceDel(addr std.Address) (success bool) { for i, entry := range s.addrs { if entry == addr { s.addrs = append(s.addrs[:i], s.addrs[i+1:]...) @@ -91,8 +84,20 @@ func (s *Set) Del(addr std.Address) (success bool) { return false } -// ReplaceAll removes all existing admins and replaces them with a new one. -// It requires the caller or prevRealm to be an admin, otherwise, it panics. +// Del removes an address from the list of authorized addresses. +// It requires the caller or prevRealm to be authorized, otherwise, it panics. +func (s *Set) Del(addr std.Address) (success bool) { + // XXX: should we prevent deleting self? + + // Prevent deleting the last authorized address. + if len(s.addrs) == 1 { + panic("cannot have no authorized address.") + } + return s.ForceDel(addr) +} + +// ReplaceAll removes all existing authorized addresses and replaces them with a new one. +// It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) ReplaceAll(addr std.Address) { s.addrs = []std.Address{addr} } diff --git a/examples/gno.land/p/demo/administrable/unprivileged.gno b/examples/gno.land/p/demo/administrable/unprivileged.gno index de37f9cf4a4..6eaaa9aaccf 100644 --- a/examples/gno.land/p/demo/administrable/unprivileged.gno +++ b/examples/gno.land/p/demo/administrable/unprivileged.gno @@ -21,6 +21,11 @@ func (u *UnprivilegedSet) Del(addr std.Address) { u.Set.Del(addr) } +func (u *UnprivilegedSet) ForceDel(addr std.Address) { + u.AssertCurrentHasAccess() + u.Set.ForceDel(addr) +} + func (u *UnprivilegedSet) ReplaceAll(addr std.Address) { u.AssertCurrentHasAccess() u.Set.ReplaceAll(addr) diff --git a/examples/gno.land/p/demo/administrable/z1_filetest.gno b/examples/gno.land/p/demo/administrable/z1_filetest.gno new file mode 100644 index 00000000000..1dba8e8149d --- /dev/null +++ b/examples/gno.land/p/demo/administrable/z1_filetest.gno @@ -0,0 +1,40 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" + + "gno.land/p/demo/administrable" + "gno.land/p/demo/testutils" +) + +var Admins = administrable.New().Unprivileged() + +func main() { + initialCaller := std.GetOrigCaller() + test1 := testutils.TestAddress("test1") + test2 := testutils.TestAddress("test2") + println(Admins.List()) + Admins.Add(test1) + println(Admins.List()) + Admins.Add(test1) + println(Admins.List()) + println(Admins.CurrentHasAccess()) + Admins.Del(initialCaller) + println(Admins.List()) + println(Admins.CurrentHasAccess()) + std.TestSetOrigCaller(test2) + println(Admins.CurrentHasAccess()) + std.TestSetOrigCaller(test1) + println(Admins.CurrentHasAccess()) +} + +// Output: +// slice[ref(a8ada09dee16d791fd406d629fe29bb0ed084a30:6)] +// slice[("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// slice[("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// true +// slice[("g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7" std.Address)] +// false +// false +// true From 85f9cd89e7ff03b431546caff3cb77eaa5833ca1 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:02:17 +0900 Subject: [PATCH 08/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../p/demo/administrable/unprivileged.gno | 2 +- examples/gno.land/r/gnoland/blog/admin.gno | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/gno.land/p/demo/administrable/unprivileged.gno b/examples/gno.land/p/demo/administrable/unprivileged.gno index 6eaaa9aaccf..cff0f866e16 100644 --- a/examples/gno.land/p/demo/administrable/unprivileged.gno +++ b/examples/gno.land/p/demo/administrable/unprivileged.gno @@ -6,7 +6,7 @@ type UnprivilegedSet struct { Set } -// Unprivileged returns an unprivileged structure that can be exposed safely. +// Unprivileged returns an unprivileged structure that can be securely exposed as object. func (s Set) Unprivileged() UnprivilegedSet { return UnprivilegedSet{Set: s} } diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 6fdb78ce061..0256467bee3 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -20,24 +20,29 @@ func init() { admins.Add("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @manfred } -func AdminSetAdminAddr(addr std.Address) { +func AdminSetInPause(state bool) { assertIsAdmin() - adminAddr = addr + inPause = state } -func AdminSetInPause(state bool) { +func AdminAddAdmin(addr std.Address) { assertIsAdmin() - inPause = state + admins.Add(addr) +} + +func AdminDelAdmin(addr std.Address) { + assertIsAdmin() + admins.Del(addr) } func AdminAddModerator(addr std.Address) { assertIsAdmin() - moderatorList.Set(addr.String(), true) + moderators.Add(addr) } -func AdminRemoveModerator(addr std.Address) { +func AdminDelModerator(addr std.Address) { assertIsAdmin() - moderatorList.Set(addr.String(), false) // FIXME: delete instead? + moderators.Del(addr) } func ModAddPost(slug, title, body, tags string) { @@ -59,12 +64,12 @@ func ModEditPost(slug, title, body, tags string) { func ModAddCommenter(addr std.Address) { assertIsModerator() - commenterList.Set(addr.String(), true) + commenters.Add(addr) } func ModDelCommenter(addr std.Address) { assertIsModerator() - commenterList.Set(addr.String(), false) // FIXME: delete instead? + commenters.Del(addr) } func ModDelComment(slug string, index int) { @@ -75,18 +80,18 @@ func ModDelComment(slug string, index int) { } func assertIsAdmin() { - admins.AssertHasAccess() + admins.AssertCurrentHasAccess() } func assertIsModerator() { - if admins.HasAccess() || moderators.HasAccess() { + if admins.CurrentHasAccess() || moderators.CurrentHasAccess() { return } panic("access restricted") } func assertIsCommenter() { - if admins.HasAccess() || moderators.HasAccess() || commenters.HasAccess() { + if admins.CurrentHasAccess() || moderators.CurrentHasAccess() || commenters.CurrentHasAccess() { return } panic("access restricted") From 83b97e42adcf67b6216d67122ec788574019f1c9 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:11:39 +0900 Subject: [PATCH 09/26] chore: support unitilized Set Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/administrable/set.gno | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/gno.land/p/demo/administrable/set.gno b/examples/gno.land/p/demo/administrable/set.gno index 7e1a063045b..69ec3bf4431 100644 --- a/examples/gno.land/p/demo/administrable/set.gno +++ b/examples/gno.land/p/demo/administrable/set.gno @@ -64,6 +64,9 @@ func (s *Set) AssertCurrentHasAccess() { // Add adds an address to the list of authorized addresses. // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Add(addr std.Address) { + if s.addrs == nil { + s.addrs = make([]std.Address) + } if s.HasAccess(addr) { // XXX: Consider panicking or handling duplicate addition differently. return From 199fbb2ade6364c7401141613678fbea533ad955 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:16:42 +0900 Subject: [PATCH 10/26] chore: rename to access Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/access/doc.gno | 3 +++ .../p/demo/{administrable => access}/example_test.gno | 4 ++-- .../gno.land/p/demo/{administrable => access}/set.gno | 2 +- .../p/demo/{administrable => access}/set_test.gno | 2 +- .../p/demo/{administrable => access}/unprivileged.gno | 2 +- .../{administrable => access}/unprivileged_test.gno | 2 +- .../p/demo/{administrable => access}/z0_filetest.gno | 6 +++--- .../p/demo/{administrable => access}/z1_filetest.gno | 4 ++-- examples/gno.land/p/demo/administrable/doc.gno | 3 --- examples/gno.land/r/gnoland/blog/admin.gno | 10 +++++----- 10 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 examples/gno.land/p/demo/access/doc.gno rename examples/gno.land/p/demo/{administrable => access}/example_test.gno (84%) rename examples/gno.land/p/demo/{administrable => access}/set.gno (99%) rename examples/gno.land/p/demo/{administrable => access}/set_test.gno (99%) rename examples/gno.land/p/demo/{administrable => access}/unprivileged.gno (96%) rename examples/gno.land/p/demo/{administrable => access}/unprivileged_test.gno (99%) rename examples/gno.land/p/demo/{administrable => access}/z0_filetest.gno (91%) rename examples/gno.land/p/demo/{administrable => access}/z1_filetest.gno (92%) delete mode 100644 examples/gno.land/p/demo/administrable/doc.gno diff --git a/examples/gno.land/p/demo/access/doc.gno b/examples/gno.land/p/demo/access/doc.gno new file mode 100644 index 00000000000..d8192d19cfe --- /dev/null +++ b/examples/gno.land/p/demo/access/doc.gno @@ -0,0 +1,3 @@ +// Access enables you to add a special account to your contract, granting exclusive privileges for certain actions. +// Its key features include easy admin initialization, administrator management, support for `std.OrigCaller` and `std.PrevRealm`, and a compact API. +package access // import "gno.land/p/demo/access" diff --git a/examples/gno.land/p/demo/administrable/example_test.gno b/examples/gno.land/p/demo/access/example_test.gno similarity index 84% rename from examples/gno.land/p/demo/administrable/example_test.gno rename to examples/gno.land/p/demo/access/example_test.gno index adee2303a61..d47083b1252 100644 --- a/examples/gno.land/p/demo/administrable/example_test.gno +++ b/examples/gno.land/p/demo/access/example_test.gno @@ -1,4 +1,4 @@ -package administrable +package access func ExamplePackageLevel() { // set this globally or during init() @@ -9,7 +9,7 @@ func ExamplePackageLevel() { } func ExampleEmbedding() { - // declare a new struct, and embed administrable.Set. + // declare a new struct, and embed access.Set. type MyObject struct { myField int Set diff --git a/examples/gno.land/p/demo/administrable/set.gno b/examples/gno.land/p/demo/access/set.gno similarity index 99% rename from examples/gno.land/p/demo/administrable/set.gno rename to examples/gno.land/p/demo/access/set.gno index 69ec3bf4431..63d6dd575a4 100644 --- a/examples/gno.land/p/demo/administrable/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -1,4 +1,4 @@ -package administrable +package access import ( "fmt" diff --git a/examples/gno.land/p/demo/administrable/set_test.gno b/examples/gno.land/p/demo/access/set_test.gno similarity index 99% rename from examples/gno.land/p/demo/administrable/set_test.gno rename to examples/gno.land/p/demo/access/set_test.gno index 6dd75a14adf..83ba3289df8 100644 --- a/examples/gno.land/p/demo/administrable/set_test.gno +++ b/examples/gno.land/p/demo/access/set_test.gno @@ -1,4 +1,4 @@ -package administrable +package access import ( "std" diff --git a/examples/gno.land/p/demo/administrable/unprivileged.gno b/examples/gno.land/p/demo/access/unprivileged.gno similarity index 96% rename from examples/gno.land/p/demo/administrable/unprivileged.gno rename to examples/gno.land/p/demo/access/unprivileged.gno index cff0f866e16..fbd223713a5 100644 --- a/examples/gno.land/p/demo/administrable/unprivileged.gno +++ b/examples/gno.land/p/demo/access/unprivileged.gno @@ -1,4 +1,4 @@ -package administrable +package access import "std" diff --git a/examples/gno.land/p/demo/administrable/unprivileged_test.gno b/examples/gno.land/p/demo/access/unprivileged_test.gno similarity index 99% rename from examples/gno.land/p/demo/administrable/unprivileged_test.gno rename to examples/gno.land/p/demo/access/unprivileged_test.gno index 6f97b4b9ee3..7d36143f6d2 100644 --- a/examples/gno.land/p/demo/administrable/unprivileged_test.gno +++ b/examples/gno.land/p/demo/access/unprivileged_test.gno @@ -1,4 +1,4 @@ -package administrable +package access import ( "std" diff --git a/examples/gno.land/p/demo/administrable/z0_filetest.gno b/examples/gno.land/p/demo/access/z0_filetest.gno similarity index 91% rename from examples/gno.land/p/demo/administrable/z0_filetest.gno rename to examples/gno.land/p/demo/access/z0_filetest.gno index 7082137cd34..45a0f9a39a6 100644 --- a/examples/gno.land/p/demo/administrable/z0_filetest.gno +++ b/examples/gno.land/p/demo/access/z0_filetest.gno @@ -4,14 +4,14 @@ package test import ( "std" - "gno.land/p/demo/administrable" + "gno.land/p/demo/access" "gno.land/p/demo/testutils" ) -var admins administrable.Set +var admins access.Set func init() { - admins = administrable.New() + admins = access.New() } func main() { diff --git a/examples/gno.land/p/demo/administrable/z1_filetest.gno b/examples/gno.land/p/demo/access/z1_filetest.gno similarity index 92% rename from examples/gno.land/p/demo/administrable/z1_filetest.gno rename to examples/gno.land/p/demo/access/z1_filetest.gno index 1dba8e8149d..4d2fc12b056 100644 --- a/examples/gno.land/p/demo/administrable/z1_filetest.gno +++ b/examples/gno.land/p/demo/access/z1_filetest.gno @@ -4,11 +4,11 @@ package test import ( "std" - "gno.land/p/demo/administrable" + "gno.land/p/demo/access" "gno.land/p/demo/testutils" ) -var Admins = administrable.New().Unprivileged() +var Admins = access.New().Unprivileged() func main() { initialCaller := std.GetOrigCaller() diff --git a/examples/gno.land/p/demo/administrable/doc.gno b/examples/gno.land/p/demo/administrable/doc.gno deleted file mode 100644 index 3246f3850b0..00000000000 --- a/examples/gno.land/p/demo/administrable/doc.gno +++ /dev/null @@ -1,3 +0,0 @@ -// Administrable enables you to add a special account to your contract, granting exclusive privileges for certain actions. -// Its key features include easy admin initialization, administrator management, support for `std.OrigCaller` and `std.PrevRealm`, and a compact API. -package administrable // import "gno.land/p/demo/administrable" diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 0256467bee3..b1a42fed8b1 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -4,19 +4,19 @@ import ( "std" "strings" - "gno.land/p/demo/administrable" + "gno.land/p/demo/access" "gno.land/p/demo/avl" ) var ( - admins administrable.Set - moderators administrable.Set - commenters administrable.Set + admins access.Set + moderators access.Set + commenters access.Set inPause bool ) func init() { - admins = administrable.New() + admins = access.New() admins.Add("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @manfred } From 8534c0369b8f3cb919fd306f61631378b170985d Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:22:20 +0900 Subject: [PATCH 11/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/access/doc.gno | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/access/doc.gno b/examples/gno.land/p/demo/access/doc.gno index d8192d19cfe..d5d3da89f2e 100644 --- a/examples/gno.land/p/demo/access/doc.gno +++ b/examples/gno.land/p/demo/access/doc.gno @@ -1,3 +1,6 @@ -// Access enables you to add a special account to your contract, granting exclusive privileges for certain actions. -// Its key features include easy admin initialization, administrator management, support for `std.OrigCaller` and `std.PrevRealm`, and a compact API. +// Package access provides a simple access control library for managing authorized addresses and controlling resource access. +// +// Features: authorized address management, access verification. +// +// Note: This package is suitable for basic access control in simple use cases. Consider specialized libraries or frameworks for advanced access control and permission management. package access // import "gno.land/p/demo/access" From fab9639e03c628a95171ad880de0a393b077437d Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:37:13 +0900 Subject: [PATCH 12/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gno.land/cmd/gnoland/main.go b/gno.land/cmd/gnoland/main.go index 0ebc3d7058a..fd9df33c8fc 100644 --- a/gno.land/cmd/gnoland/main.go +++ b/gno.land/cmd/gnoland/main.go @@ -231,6 +231,7 @@ func makeGenesisDoc( for _, path := range []string{ "p/demo/ufmt", "p/demo/avl", + "p/demo/access", "p/demo/grc/exts", "p/demo/grc/grc20", "p/demo/grc/grc721", From 48e958b2ae1fce122d39e819f0a36d766075175a Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 8 Jun 2023 21:40:09 +0900 Subject: [PATCH 13/26] chore: fixup Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/access/set.gno | 7 ++----- examples/gno.land/r/gnoland/blog/admin.gno | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 63d6dd575a4..20832209d53 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -1,9 +1,6 @@ package access -import ( - "fmt" - "std" -) +import "std" // New returns a Set object initialized with the caller as the unique authorized address. // It is recommended to use this function in the `init()` function of the calling realm. @@ -65,7 +62,7 @@ func (s *Set) AssertCurrentHasAccess() { // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Add(addr std.Address) { if s.addrs == nil { - s.addrs = make([]std.Address) + s.addrs = make([]std.Address, 0) } if s.HasAccess(addr) { // XXX: Consider panicking or handling duplicate addition differently. diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index b1a42fed8b1..3db9fe0b0c7 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -5,7 +5,6 @@ import ( "strings" "gno.land/p/demo/access" - "gno.land/p/demo/avl" ) var ( From f58167ad0df1cf7b2ea601fef11ac9a80062f407 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Tue, 20 Jun 2023 17:41:48 +0200 Subject: [PATCH 14/26] chore: fixup --- examples/gno.land/p/demo/access/gno.mod | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 examples/gno.land/p/demo/access/gno.mod diff --git a/examples/gno.land/p/demo/access/gno.mod b/examples/gno.land/p/demo/access/gno.mod new file mode 100644 index 00000000000..b7752f33dca --- /dev/null +++ b/examples/gno.land/p/demo/access/gno.mod @@ -0,0 +1,5 @@ +module gno.land/p/demo/access + +require ( + "gno.land/p/demo/testutils" v0.0.0-latest +) From be4449a37cba7fdc719f21f2648af58066f3797d Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:03:28 -0400 Subject: [PATCH 15/26] Update examples/gno.land/p/demo/access/set.gno Co-authored-by: Antonio Navarro Perez --- examples/gno.land/p/demo/access/set.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 20832209d53..03a1b5b998e 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -13,9 +13,9 @@ func New() Set { // NewWithAddress returns a Set object initialized with the provided address as authorized. func NewWithAddress(addr std.Address) Set { - s := New() - s.ReplaceAll(addr) - return s + return Set{ + addrs: []std.Address{addr}, + } } // Set is an object containing the configuration and allowing the application of filters. From 52774200f188b9006a2b2a93aa08b9f47e08e87b Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:04:13 -0400 Subject: [PATCH 16/26] Update examples/gno.land/p/demo/access/set.gno Co-authored-by: Hariom Verma --- examples/gno.land/p/demo/access/set.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 03a1b5b998e..99acd186528 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -98,6 +98,6 @@ func (s *Set) Del(addr std.Address) (success bool) { // ReplaceAll removes all existing authorized addresses and replaces them with a new one. // It requires the caller or prevRealm to be authorized, otherwise, it panics. -func (s *Set) ReplaceAll(addr std.Address) { +func (s *Set) Replace(addr []std.Address) { s.addrs = []std.Address{addr} } From 4348288509c7dc89e9275c2f21ea99899e4a5f47 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:04:53 -0400 Subject: [PATCH 17/26] Update examples/gno.land/p/demo/access/set.gno --- examples/gno.land/p/demo/access/set.gno | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 99acd186528..f93c9d86393 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -61,9 +61,6 @@ func (s *Set) AssertCurrentHasAccess() { // Add adds an address to the list of authorized addresses. // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Add(addr std.Address) { - if s.addrs == nil { - s.addrs = make([]std.Address, 0) - } if s.HasAccess(addr) { // XXX: Consider panicking or handling duplicate addition differently. return From d66bb2bb9257970affba0a4d885adb22e42011c5 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:10:31 -0400 Subject: [PATCH 18/26] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/access/set.gno | 4 ++-- examples/gno.land/p/demo/access/unprivileged.gno | 5 +++-- examples/gno.land/r/gnoland/blog/gno.mod | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index f93c9d86393..9f03d8c03d9 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -95,6 +95,6 @@ func (s *Set) Del(addr std.Address) (success bool) { // ReplaceAll removes all existing authorized addresses and replaces them with a new one. // It requires the caller or prevRealm to be authorized, otherwise, it panics. -func (s *Set) Replace(addr []std.Address) { - s.addrs = []std.Address{addr} +func (s *Set) Replace(addrs []std.Address) { + s.addrs = addrs } diff --git a/examples/gno.land/p/demo/access/unprivileged.gno b/examples/gno.land/p/demo/access/unprivileged.gno index fbd223713a5..ac0c5bfdd8b 100644 --- a/examples/gno.land/p/demo/access/unprivileged.gno +++ b/examples/gno.land/p/demo/access/unprivileged.gno @@ -26,7 +26,8 @@ func (u *UnprivilegedSet) ForceDel(addr std.Address) { u.Set.ForceDel(addr) } -func (u *UnprivilegedSet) ReplaceAll(addr std.Address) { +func (u *UnprivilegedSet) Replace(addr std.Address) { u.AssertCurrentHasAccess() - u.Set.ReplaceAll(addr) + addrs := []std.Address{addr} + u.Set.Replace(addrs) } diff --git a/examples/gno.land/r/gnoland/blog/gno.mod b/examples/gno.land/r/gnoland/blog/gno.mod index a8b4f3ceaa9..96a80ed4346 100644 --- a/examples/gno.land/r/gnoland/blog/gno.mod +++ b/examples/gno.land/r/gnoland/blog/gno.mod @@ -3,4 +3,5 @@ module gno.land/r/gnoland/blog require ( "gno.land/p/demo/avl" v0.0.0-latest "gno.land/p/demo/blog" v0.0.0-latest + "gno.land/p/demo/access" v0.0.0-latest ) From daf34a0ecf7d541c24c75bb4a8a30832566062c7 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:12:32 -0400 Subject: [PATCH 19/26] chore: add assertion Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/access/set.gno | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 9f03d8c03d9..4d189bf3966 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -84,6 +84,10 @@ func (s *Set) ForceDel(addr std.Address) (success bool) { // Del removes an address from the list of authorized addresses. // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Del(addr std.Address) (success bool) { + if len(s.addrs) == 0 { + panic("should not happen") + } + // XXX: should we prevent deleting self? // Prevent deleting the last authorized address. From 8fe571fb7a01bc855b78484a028c41c2ed925915 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:56:19 -0400 Subject: [PATCH 20/26] fix: init ctx of injected package when using gno test to support lazy imports containing an init() func Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gnovm/tests/file.go | 21 +++++++++++++-------- gnovm/tests/imports.go | 3 +++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 451bf0677dc..9ba47ebd078 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -34,6 +34,18 @@ func TestMachine(store gno.Store, stdout io.Writer, pkgPath string) *gno.Machine } func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAlloc int64, send std.Coins) *gno.Machine { + ctx := testContext(pkgPath, send) + m := gno.NewMachineWithOptions(gno.MachineOptions{ + PkgPath: "", // set later. + Output: stdout, + Store: store, + Context: ctx, + MaxAllocBytes: maxAlloc, + }) + return m +} + +func testContext(pkgPath string, send std.Coins) stdlibs.ExecContext { // FIXME: create a better package to manage this, with custom constructors pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called. caller := gno.DerivePkgAddr("user1.gno") @@ -51,14 +63,7 @@ func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAll OrigSendSpent: new(std.Coins), Banker: banker, } - m := gno.NewMachineWithOptions(gno.MachineOptions{ - PkgPath: "", // set later. - Output: stdout, - Store: store, - Context: ctx, - MaxAllocBytes: maxAlloc, - }) - return m + return ctx } type runFileTestOptions struct { diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index fc2820ce00e..41e9e1787f7 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -440,10 +440,13 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri panic(fmt.Sprintf("found an empty package %q", pkgPath)) } + send := std.Coins{} + ctx := testContext(pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ PkgPath: "test", Output: stdout, Store: store, + Context: ctx, }) pn, pv = m2.RunMemPackage(memPkg, true) return From 5d3d8185544d9d07e8e6b47b86fc9ab60f5db568 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sat, 21 Oct 2023 10:25:06 -0400 Subject: [PATCH 21/26] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/r/manfred/present/present_miami23_filetest.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/manfred/present/present_miami23_filetest.gno b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno index 05c41905060..2834345c1bb 100644 --- a/examples/gno.land/r/manfred/present/present_miami23_filetest.gno +++ b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno @@ -54,4 +54,4 @@ func main() { // // [#demo](/r/manfred/present:t/demo) [#portal-loop](/r/manfred/present:t/portal-loop) [#miami](/r/manfred/present:t/miami) // -// by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 1970-01-01 12:00am UTC +// by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 2009-02-13 11:31pm UTC From d9e0302cd048c1bef0dc2843ebf610315ef169a4 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:09:36 +0200 Subject: [PATCH 22/26] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gnovm/tests/file.go | 25 ++++++++++++++++--------- gnovm/tests/imports.go | 42 ++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 392b5dfe425..852bb74d198 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -15,8 +15,10 @@ import ( gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/stdlibs" + teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std" "github.com/gnolang/gno/tm2/pkg/crypto" osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/std" "github.com/pmezard/go-difflib/difflib" ) @@ -34,7 +36,7 @@ func TestMachine(store gno.Store, stdout io.Writer, pkgPath string) *gno.Machine } func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAlloc int64, send std.Coins) *gno.Machine { - ctx := testContext(pkgPath, send) + ctx := TestContext(pkgPath, send) m := gno.NewMachineWithOptions(gno.MachineOptions{ PkgPath: "", // set later. Output: stdout, @@ -45,7 +47,8 @@ func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAll return m } -func testContext(pkgPath string, send std.Coins) stdlibs.ExecContext { +// TestContext returns a TestExecContext. Usable for test purpose only. +func TestContext(pkgPath string, send std.Coins) *teststd.TestExecContext { // FIXME: create a better package to manage this, with custom constructors pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called. caller := gno.DerivePkgAddr("user1.gno") @@ -62,8 +65,12 @@ func testContext(pkgPath string, send std.Coins) stdlibs.ExecContext { OrigSend: send, OrigSendSpent: new(std.Coins), Banker: banker, + EventLogger: sdk.NewEventLogger(), + } + return &teststd.TestExecContext{ + ExecContext: ctx, + RealmFrames: make(map[*gno.Frame]teststd.RealmOverride), } - return ctx } type runFileTestOptions struct { @@ -134,14 +141,13 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { defer func() { if r := recover(); r != nil { // print output. - fmt.Println("OUTPUT:\n", stdout.String()) - // print stack if unexpected error. + fmt.Printf("OUTPUT:\n%s\n", stdout.String()) pnc = r - if errWanted == "" { - rtdb.PrintStack() - } err := strings.TrimSpace(fmt.Sprintf("%v", pnc)) - if !strings.Contains(err, errWanted) { + // print stack if unexpected error. + if errWanted == "" || + !strings.Contains(err, errWanted) { + fmt.Printf("ERROR:\n%s\n", err) // error didn't match: print stack // NOTE: will fail testcase later. rtdb.PrintStack() @@ -256,6 +262,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { default: errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } + if errstr != errWanted { panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, errstr, errWanted)) } diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index a5ed0e25b8e..6a3e6ab2bbb 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -29,7 +29,6 @@ import ( "os" "path/filepath" "reflect" - "sort" "strconv" "strings" "sync" @@ -39,10 +38,11 @@ import ( "unicode/utf8" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/stdlibs" teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs" - dbm "github.com/gnolang/gno/tm2/pkg/db" + teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std" + "github.com/gnolang/gno/tm2/pkg/db/memdb" osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" "github.com/gnolang/gno/tm2/pkg/store/iavl" stypes "github.com/gnolang/gno/tm2/pkg/store/types" @@ -62,7 +62,7 @@ const ( // NOTE: this isn't safe, should only be used for testing. func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (store gno.Store) { - getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) { + getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) { if pkgPath == "" { panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter")) } @@ -78,10 +78,13 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri if strings.HasPrefix(pkgPath, testPath) { baseDir := filepath.Join(filesPath, "extern", pkgPath[len(testPath):]) memPkg := gno.ReadMemPackage(baseDir, pkgPath) + send := std.Coins{} + ctx := TestContext(pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ PkgPath: "test", Output: stdout, - Store: store, + Store: newStore, + Context: ctx, }) // pkg := gno.NewPackageNode(gno.Name(memPkg.Name), memPkg.Path, nil) // pv := pkg.NewPackage() @@ -93,7 +96,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if stdlibs package is preferred , try to load it first. if mode == ImportModeStdlibsOnly || mode == ImportModeStdlibsPreferred { - pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout) if pn != nil { return } @@ -183,7 +186,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri d := arg0.GetInt64() sec := d / int64(time.Second) nano := d % int64(time.Second) - ctx := m.Context.(stdlibs.ExecContext) + ctx := m.Context.(*teststd.TestExecContext) ctx.Timestamp += sec ctx.TimestampNano += nano if ctx.TimestampNano >= int64(time.Second) { @@ -254,6 +257,16 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg.DefineGoNativeValue("Pi", math.Pi) pkg.DefineGoNativeValue("MaxFloat32", math.MaxFloat32) pkg.DefineGoNativeValue("MaxFloat64", math.MaxFloat64) + pkg.DefineGoNativeValue("MaxUint32", uint32(math.MaxUint32)) + pkg.DefineGoNativeValue("MaxUint64", uint64(math.MaxUint64)) + pkg.DefineGoNativeValue("MinInt8", math.MinInt8) + pkg.DefineGoNativeValue("MinInt16", math.MinInt16) + pkg.DefineGoNativeValue("MinInt32", math.MinInt32) + pkg.DefineGoNativeValue("MinInt64", int64(math.MinInt64)) + pkg.DefineGoNativeValue("MaxInt8", math.MaxInt8) + pkg.DefineGoNativeValue("MaxInt16", math.MaxInt16) + pkg.DefineGoNativeValue("MaxInt32", math.MaxInt32) + pkg.DefineGoNativeValue("MaxInt64", int64(math.MaxInt64)) return pkg, pkg.NewPackage() case "math/rand": // XXX only expose for tests. @@ -318,11 +331,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg := gno.NewPackageNode("big", pkgPath, nil) pkg.DefineGoNativeValue("NewInt", big.NewInt) return pkg, pkg.NewPackage() - case "sort": - pkg := gno.NewPackageNode("sort", pkgPath, nil) - pkg.DefineGoNativeValue("Strings", sort.Strings) - // pkg.DefineGoNativeValue("Sort", sort.Sort) - return pkg, pkg.NewPackage() case "flag": pkg := gno.NewPackageNode("flag", pkgPath, nil) pkg.DefineGoNativeType(reflect.TypeOf(flag.Flag{})) @@ -367,7 +375,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if native package is preferred, try to load stdlibs/* as backup. if mode == ImportModeNativePreferred { - pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout) if pn != nil { return } @@ -382,11 +390,11 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri } send := std.Coins{} - ctx := testContext(pkgPath, send) + ctx := TestContext(pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ PkgPath: "test", Output: stdout, - Store: store, + Store: newStore, Context: ctx, }) pn, pv = m2.RunMemPackage(memPkg, true) @@ -395,7 +403,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri return nil, nil } // NOTE: store is also used in closure above. - db := dbm.NewMemDB() + db := memdb.NewMemDB() baseStore := dbadapter.StoreConstructor(db, stypes.StoreOptions{}) iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{}) store = gno.NewStore(nil, baseStore, iavlStore) @@ -403,8 +411,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri store.SetNativeStore(teststdlibs.NativeStore) store.SetPackageInjector(testPackageInjector) store.SetStrictGo2GnoMapping(false) - // native mappings - stdlibs.InjectNativeMappings(store) return } From 45da03c12af3338089a98489361f9dddcf597b81 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:12:09 +0200 Subject: [PATCH 23/26] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../present/present_miami23_filetest.gno | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/examples/gno.land/r/manfred/present/present_miami23_filetest.gno b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno index d473c17b06c..ac19d83ade4 100644 --- a/examples/gno.land/r/manfred/present/present_miami23_filetest.gno +++ b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno @@ -9,62 +9,3 @@ func main() { println("------------------------------------") println(present.Render("p/miami23")) } - -// Output: -//
-// -// ### [Portal Loop Demo (Miami 2023)](/r/manfred/present:p/miami23) -// 15 Oct 2023 -//
-// ------------------------------------ -//
-// -// # Portal Loop Demo (Miami 2023) -// -// Rendered by Gno. -// -// [Source (WIP)](https://github.com/gnolang/gno/pull/1176) -// -// ## Portal Loop -// -// - DONE: Dynamic homepage, key pages, aliases, and redirects. -// - TODO: Deploy with history, complete worxdao v0. -// - Will replace the static gno.land site. -// - Enhances local development. -// -// [GitHub Issue](https://github.com/gnolang/gno/issues/1108) -// -// ## Roadmap -// -// - Crafting the roadmap this week, open to collaboration. -// - Combining onchain (portal loop) and offchain (GitHub). -// - Next week: Unveiling the official v0 roadmap. -// -// ## Teams, DAOs, Projects -// -// - Developing worxDAO contracts for directories of projects and teams. -// - GitHub teams and projects align with this structure. -// - CODEOWNER file updates coming. -// - Initial teams announced next week. -// -// ## Tech Team Retreat Plan -// -// - Continue Portal Loop. -// - Consider dApp development. -// - Explore new topics [here](https://github.com/orgs/gnolang/projects/15/). -// - Engage in workshops. -// - Connect and have fun with colleagues. -// -// --- -// -// Tags: [#demo](/r/manfred/present:t/demo) [#portal-loop](/r/manfred/present:t/portal-loop) [#miami](/r/manfred/present:t/miami) -// -// Written by moul on 15 Oct 2023 -// -// Published by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq to Manfred's Presentations -// -// --- -//
Comment section -// -//
-//
From 7d878bb3960fb1b379046ebb5139b2144f968fda Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 12 Sep 2024 11:02:20 +0200 Subject: [PATCH 24/26] reset --- examples/gno.land/p/demo/access/set.gno | 85 +++++++++++++------------ 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 4d189bf3966..8fea4e2c063 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -1,60 +1,63 @@ package access -import "std" - -// New returns a Set object initialized with the caller as the unique authorized address. -// It is recommended to use this function in the `init()` function of the calling realm. -func New() Set { - addr := std.GetOrigCaller() - return Set{ - addrs: []std.Address{addr}, - } +import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/ownable" + "github.com/gnolang/gno/gnovm/stdlibs/std" +) + +// Set is an object containing the configuration and allowing the application of filters +// It is suited to be used as a contract-side global variable or can be embedded in another Go object +type Set struct { + *ownable.Ownable // owner in ownable is superuser + authorized *avl.Tree // std.Addr > struct{}{} } -// NewWithAddress returns a Set object initialized with the provided address as authorized. -func NewWithAddress(addr std.Address) Set { - return Set{ - addrs: []std.Address{addr}, +// New returns a Set object initialized with the caller as the unique authorized address +// It is recommended to use this function in the `init()` function of the calling realm +func New() *Set { + a := &Set{ + ownable.New(), + avl.NewTree(), } -} - -// Set is an object containing the configuration and allowing the application of filters. -// It is suited to be used as a contract-side global variable or can be embedded in another Go object. -type Set struct { - addrs []std.Address -} -// List returns a slice containing all the authorized addresses. -func (s *Set) List() []std.Address { - return s.addrs + // Add owner to auth list + a.authorized.Set(a.Owner().String(), struct{}{}) + return a } -// HasAccess checks if the provided address is in the list of authorized ones. -func (s *Set) HasAccess(addr std.Address) bool { - for _, entry := range s.addrs { - if entry == addr { - return true - } +// NewWithAddress returns a Set object initialized with the provided address as authorized +func NewWithAddress(addr std.Address) *Set { + a := &Set{ + ownable.NewWithAddress(addr), + avl.NewTree(), } - return false + + // Add owner to the set + a.authorized.Set(a.Owner().String(), struct{}{}) + return a } -// CurrentHasAccess checks if the caller or prevRealm is authorized. -func (s *Set) CurrentHasAccess() bool { - if s.HasAccess(std.GetOrigCaller()) { - return true - } +//// List returns the AVL tree containing all the authorized addresses +//func (s Set) List() avl.Tree { +// return *s.authorized +//} - // XXX: also check for std.PrevRealm, when merged. +// HasAccess checks if the provided address is in the list of authorized ones. +func (s Set) HasAccess(addr std.Address) bool { + return s.authorized.Has(addr.String()) +} - return false +// CallerHasAccess checks if the caller or prevRealm is authorized. +func (s Set) CallerHasAccess() bool { + return s.HasAccess(std.PrevRealm().Addr()) } -// AssertCurrentHasAccess checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as authorized. +// AssertCallerHasAccess checks whether the std.GetOrigCaller or std.PrevRealm is whitelisted as authorized. // If not, it panics indicating restricted access. -func (s *Set) AssertCurrentHasAccess() { - if !s.CurrentHasAccess() { - panic("restricted access.") +func (s *Set) AssertCallerHasAccess() { + if !s.CallerHasAccess() { + panic("access: caller not in set") } } From 2b13382b393619342635534f602222c12f211f9d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 17 Sep 2024 20:13:20 +0200 Subject: [PATCH 25/26] save --- examples/gno.land/p/demo/access/errors.gno | 9 +++ examples/gno.land/p/demo/access/set.gno | 76 ++++++++++++---------- 2 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 examples/gno.land/p/demo/access/errors.gno diff --git a/examples/gno.land/p/demo/access/errors.gno b/examples/gno.land/p/demo/access/errors.gno new file mode 100644 index 00000000000..50da33c237d --- /dev/null +++ b/examples/gno.land/p/demo/access/errors.gno @@ -0,0 +1,9 @@ +package access + +import "errors" + +var ( + ErrNotInSet = errors.New("access: address not in set") + ErrNoMembersInSet = errors.New("access: set is empty") + ErrLastMember = errors.New("access: cannot delete last member") +) diff --git a/examples/gno.land/p/demo/access/set.gno b/examples/gno.land/p/demo/access/set.gno index 8fea4e2c063..bd8fdc7054d 100644 --- a/examples/gno.land/p/demo/access/set.gno +++ b/examples/gno.land/p/demo/access/set.gno @@ -16,32 +16,44 @@ type Set struct { // New returns a Set object initialized with the caller as the unique authorized address // It is recommended to use this function in the `init()` function of the calling realm func New() *Set { - a := &Set{ + s := &Set{ ownable.New(), avl.NewTree(), } // Add owner to auth list - a.authorized.Set(a.Owner().String(), struct{}{}) - return a + s.authorized.Set(a.Owner().String(), struct{}{}) + return s } // NewWithAddress returns a Set object initialized with the provided address as authorized func NewWithAddress(addr std.Address) *Set { - a := &Set{ + s := &Set{ ownable.NewWithAddress(addr), avl.NewTree(), } // Add owner to the set - a.authorized.Set(a.Owner().String(), struct{}{}) - return a + s.authorized.Set(a.Owner().String(), struct{}{}) + return s } -//// List returns the AVL tree containing all the authorized addresses -//func (s Set) List() avl.Tree { -// return *s.authorized -//} +// List returns a list containing all the authorized addresses +func (s Set) List() []std.Address { + var addrs []std.Address + if s.authorized.Size() == 0 { + return addrs + } + // todo test + s.authorized.Iterate("", "", func(key string, value interface{}) bool { + if value == struct{}{} { + addrs = append(addrs, std.Address(key)) + } + return false + }) + + return addrs +} // HasAccess checks if the provided address is in the list of authorized ones. func (s Set) HasAccess(addr std.Address) bool { @@ -57,7 +69,7 @@ func (s Set) CallerHasAccess() bool { // If not, it panics indicating restricted access. func (s *Set) AssertCallerHasAccess() { if !s.CallerHasAccess() { - panic("access: caller not in set") + panic("access: caller is not authorized") } } @@ -65,41 +77,37 @@ func (s *Set) AssertCallerHasAccess() { // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Add(addr std.Address) { if s.HasAccess(addr) { - // XXX: Consider panicking or handling duplicate addition differently. - return + panic("access: addr already has access") } - s.addrs = append(s.addrs, addr) -} -// ForceDel removes an address from the list of authorized addresses. -// It won't panic if there is only one address. -// It requires the caller or prevRealm to be authorized, otherwise, it panics. -func (s *Set) ForceDel(addr std.Address) (success bool) { - for i, entry := range s.addrs { - if entry == addr { - s.addrs = append(s.addrs[:i], s.addrs[i+1:]...) - return true - } - } - return false + s.authorized.Set(addr.String(), struct{}{}) } // Del removes an address from the list of authorized addresses. // It requires the caller or prevRealm to be authorized, otherwise, it panics. -func (s *Set) Del(addr std.Address) (success bool) { - if len(s.addrs) == 0 { - panic("should not happen") +func (s *Set) Del(addr std.Address) error { + if s.authorized.Size() == 0 { + return ErrNoMembersInSet } - // XXX: should we prevent deleting self? - - // Prevent deleting the last authorized address. - if len(s.addrs) == 1 { - panic("cannot have no authorized address.") + if s.authorized.Size() == 1 { + return ErrLastMember } + return s.ForceDel(addr) } +// ForceDel removes an address from the list of authorized addresses. +// It will not panic if the last member of the set is removed +// WARN: In this case, the ownership of the set is fully removed. +func (s *Set) ForceDel(addr std.Address) error { + if _, removed := s.authorized.Remove(addr.String()); !removed { + return ErrNotInSet + } + + return nil +} + // ReplaceAll removes all existing authorized addresses and replaces them with a new one. // It requires the caller or prevRealm to be authorized, otherwise, it panics. func (s *Set) Replace(addrs []std.Address) { From 821cdf8fa858964e8401863851da1e69e700d5d8 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 23 Sep 2024 14:05:35 +0200 Subject: [PATCH 26/26] save --- .../exts/authorizable/authorizable.gno | 24 +++++++++++++++---- examples/gno.land/p/demo/ownable/ownable.gno | 12 ++++------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index f9f0ea15dd9..d64e3e22fb8 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -6,22 +6,33 @@ package authorizable import ( - "std" - - "gno.land/p/demo/avl" - "gno.land/p/demo/ownable" - "gno.land/p/demo/ufmt" + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/ownable" + "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + "github.com/gnolang/gno/gnovm/stdlibs/std" ) type Authorizable struct { *ownable.Ownable // owner in ownable is superuser authorized *avl.Tree // std.Addr > struct{}{} + pendingOps *avl.Tree // std.Addr > vote + addThreshold int // number of votes needed to add another member + delThreshold int // number of votes needed to delete a member +} + +// vote stores number of signatures for an operation on an address +type vote struct { + del int + add int } func NewAuthorizable() *Authorizable { a := &Authorizable{ ownable.New(), avl.NewTree(), + avl.NewTree(), + 0, + 0, } // Add owner to auth list @@ -33,6 +44,9 @@ func NewAuthorizableWithAddress(addr std.Address) *Authorizable { a := &Authorizable{ ownable.NewWithAddress(addr), avl.NewTree(), + avl.NewTree(), + 0, + 0, } // Add owner to auth list diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index a77b22461a9..605c12169c6 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -1,6 +1,6 @@ package ownable -import "std" +import "github.com/gnolang/gno/gnovm/stdlibs/std" const OwnershipTransferEvent = "OwnershipTransfer" @@ -71,17 +71,13 @@ func (o Ownable) Owner() std.Address { } // CallerIsOwner checks if the caller of the function is the Realm's owner -func (o Ownable) CallerIsOwner() error { - if std.PrevRealm().Addr() == o.owner { - return nil - } - - return ErrUnauthorized +func (o Ownable) CallerIsOwner() bool { + return std.PrevRealm().Addr() == o.owner } // AssertCallerIsOwner panics if the caller is not the owner func (o Ownable) AssertCallerIsOwner() { - if std.PrevRealm().Addr() != o.owner { + if !o.CallerIsOwner() { panic(ErrUnauthorized) } }