Skip to content

Commit

Permalink
feat(examples): expose ownable & pausable safe objects, update ownabl…
Browse files Browse the repository at this point in the history
…e API (gnolang#3331)

## Description

This PR exposes safe objects where possible for ownable and pausable
packages.

Let's start exposing variables named `Ownable`, ie:

```go
var Ownable = ownable.New()
```

This is the intended use, as exposing this field allows direct, safe,
MsgRun calls to access the ownable functions. It's also impossible to
directly reassign the value of this variable from another realm.

This PR also introduces a BREAKING CHANGE: the `Ownable.CallerIsOwner`
API now returns a boolean instead of an error, which makes more sense
considering the name of the function.

---------

Co-authored-by: Guilhem Fanton <[email protected]>
  • Loading branch information
2 people authored and omarsy committed Dec 18, 2024
1 parent 198e883 commit 158e545
Show file tree
Hide file tree
Showing 19 changed files with 82 additions and 137 deletions.
4 changes: 2 additions & 2 deletions examples/gno.land/p/demo/memeland/memeland.gno
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ func (m *Memeland) RemovePost(id string) string {
panic("id cannot be empty")
}

if err := m.CallerIsOwner(); err != nil {
panic(err)
if !m.CallerIsOwner() {
panic(ownable.ErrUnauthorized)
}

for i, post := range m.Posts {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func NewAuthorizableWithAddress(addr std.Address) *Authorizable {
}

func (a *Authorizable) AddToAuthList(addr std.Address) error {
if err := a.CallerIsOwner(); err != nil {
if !a.CallerIsOwner() {
return ErrNotSuperuser
}

Expand All @@ -55,7 +55,7 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error {
}

func (a *Authorizable) DeleteFromAuthList(addr std.Address) error {
if err := a.CallerIsOwner(); err != nil {
if !a.CallerIsOwner() {
return ErrNotSuperuser
}

Expand Down
19 changes: 7 additions & 12 deletions examples/gno.land/p/demo/ownable/ownable.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const OwnershipTransferEvent = "OwnershipTransfer"

// Ownable is meant to be used as a top-level object to make your contract ownable OR
// being embedded in a Gno object to manage per-object ownership.
// Ownable is safe to export as a top-level object
type Ownable struct {
owner std.Address
}
Expand All @@ -24,9 +25,8 @@ func NewWithAddress(addr std.Address) *Ownable {

// TransferOwnership transfers ownership of the Ownable struct to a new address
func (o *Ownable) TransferOwnership(newOwner std.Address) error {
err := o.CallerIsOwner()
if err != nil {
return err
if !o.CallerIsOwner() {
return ErrUnauthorized
}

if !newOwner.IsValid() {
Expand All @@ -48,9 +48,8 @@ func (o *Ownable) TransferOwnership(newOwner std.Address) error {
// Top-level usage: disables all only-owner actions/functions,
// Embedded usage: behaves like a burn functionality, removing the owner from the struct
func (o *Ownable) DropOwnership() error {
err := o.CallerIsOwner()
if err != nil {
return err
if !o.CallerIsOwner() {
return ErrUnauthorized
}

prevOwner := o.owner
Expand All @@ -71,12 +70,8 @@ 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
Expand Down
32 changes: 10 additions & 22 deletions examples/gno.land/p/demo/ownable/ownable_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"gno.land/p/demo/testutils"
"gno.land/p/demo/uassert"
"gno.land/p/demo/urequire"
)

var (
Expand All @@ -19,18 +20,14 @@ func TestNew(t *testing.T) {

o := New()
got := o.Owner()
if alice != got {
t.Fatalf("Expected %s, got: %s", alice, got)
}
uassert.Equal(t, got, alice)
}

func TestNewWithAddress(t *testing.T) {
o := NewWithAddress(alice)

got := o.Owner()
if alice != got {
t.Fatalf("Expected %s, got: %s", alice, got)
}
uassert.Equal(t, got, alice)
}

func TestTransferOwnership(t *testing.T) {
Expand All @@ -39,14 +36,11 @@ func TestTransferOwnership(t *testing.T) {
o := New()

err := o.TransferOwnership(bob)
if err != nil {
t.Fatalf("TransferOwnership failed, %v", err)
}
urequire.NoError(t, err)

got := o.Owner()
if bob != got {
t.Fatalf("Expected: %s, got: %s", bob, got)
}

uassert.Equal(t, got, bob)
}

func TestCallerIsOwner(t *testing.T) {
Expand All @@ -58,8 +52,7 @@ func TestCallerIsOwner(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(unauthorizedCaller))
std.TestSetOrigCaller(unauthorizedCaller) // TODO(bug): should not be needed

err := o.CallerIsOwner()
uassert.Error(t, err) // XXX: IsError(..., unauthorizedCaller)
uassert.False(t, o.CallerIsOwner())
}

func TestDropOwnership(t *testing.T) {
Expand All @@ -68,7 +61,7 @@ func TestDropOwnership(t *testing.T) {
o := New()

err := o.DropOwnership()
uassert.NoError(t, err, "DropOwnership failed")
urequire.NoError(t, err, "DropOwnership failed")

owner := o.Owner()
uassert.Empty(t, owner, "owner should be empty")
Expand All @@ -85,13 +78,8 @@ func TestErrUnauthorized(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(bob))
std.TestSetOrigCaller(bob) // TODO(bug): should not be needed

err := o.TransferOwnership(alice)
if err != ErrUnauthorized {
t.Fatalf("Should've been ErrUnauthorized, was %v", err)
}

err = o.DropOwnership()
uassert.ErrorContains(t, err, ErrUnauthorized.Error())
uassert.ErrorContains(t, o.TransferOwnership(alice), ErrUnauthorized.Error())
uassert.ErrorContains(t, o.DropOwnership(), ErrUnauthorized.Error())
}

func TestErrInvalidAddress(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions examples/gno.land/p/demo/pausable/pausable.gno
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ func (p Pausable) IsPaused() bool {

// Pause sets the state of Pausable to true, meaning all pausable functions are paused
func (p *Pausable) Pause() error {
if err := p.CallerIsOwner(); err != nil {
return err
if !p.CallerIsOwner() {
return ownable.ErrUnauthorized
}

p.paused = true
Expand All @@ -46,8 +46,8 @@ func (p *Pausable) Pause() error {

// Unpause sets the state of Pausable to false, meaning all pausable functions are resumed
func (p *Pausable) Unpause() error {
if err := p.CallerIsOwner(); err != nil {
return err
if !p.CallerIsOwner() {
return ownable.ErrUnauthorized
}

p.paused = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (ls *LifetimeSubscription) HasValidSubscription(addr std.Address) error {

// UpdateAmount allows the owner of the LifetimeSubscription contract to update the subscription price.
func (ls *LifetimeSubscription) UpdateAmount(newAmount int64) error {
if err := ls.CallerIsOwner(); err != nil {
if !ls.CallerIsOwner() {
return ErrNotAuthorized
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (rs *RecurringSubscription) GetExpiration(addr std.Address) (time.Time, err

// UpdateAmount allows the owner of the subscription contract to change the required subscription amount.
func (rs *RecurringSubscription) UpdateAmount(newAmount int64) error {
if err := rs.CallerIsOwner(); err != nil {
if !rs.CallerIsOwner() {
return ErrNotAuthorized
}

Expand Down
16 changes: 8 additions & 8 deletions examples/gno.land/p/n2p5/mgroup/mgroup.gno
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func New(ownerAddress std.Address) *ManagedGroup {
// AddBackupOwner adds a backup owner to the group by std.Address.
// If the caller is not the owner, an error is returned.
func (g *ManagedGroup) AddBackupOwner(addr std.Address) error {
if err := g.owner.CallerIsOwner(); err != nil {
return err
if !g.owner.CallerIsOwner() {
return ownable.ErrUnauthorized
}
if !addr.IsValid() {
return ErrInvalidAddress
Expand All @@ -57,8 +57,8 @@ func (g *ManagedGroup) AddBackupOwner(addr std.Address) error {
// RemoveBackupOwner removes a backup owner from the group by std.Address.
// The owner cannot be removed. If the caller is not the owner, an error is returned.
func (g *ManagedGroup) RemoveBackupOwner(addr std.Address) error {
if err := g.owner.CallerIsOwner(); err != nil {
return err
if !g.owner.CallerIsOwner() {
return ownable.ErrUnauthorized
}
if !addr.IsValid() {
return ErrInvalidAddress
Expand Down Expand Up @@ -90,8 +90,8 @@ func (g *ManagedGroup) ClaimOwnership() error {
// AddMember adds a member to the group by std.Address.
// If the caller is not the owner, an error is returned.
func (g *ManagedGroup) AddMember(addr std.Address) error {
if err := g.owner.CallerIsOwner(); err != nil {
return err
if !g.owner.CallerIsOwner() {
return ownable.ErrUnauthorized
}
if !addr.IsValid() {
return ErrInvalidAddress
Expand All @@ -104,8 +104,8 @@ func (g *ManagedGroup) AddMember(addr std.Address) error {
// The owner cannot be removed. If the caller is not the owner,
// an error is returned.
func (g *ManagedGroup) RemoveMember(addr std.Address) error {
if err := g.owner.CallerIsOwner(); err != nil {
return err
if !g.owner.CallerIsOwner() {
return ownable.ErrUnauthorized
}
if !addr.IsValid() {
return ErrInvalidAddress
Expand Down
8 changes: 4 additions & 4 deletions examples/gno.land/r/demo/foo20/foo20.gno
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import (
var (
Token, privateLedger = grc20.NewToken("Foo", "FOO", 4)
UserTeller = Token.CallerTeller()
owner = ownable.NewWithAddress("g1manfred47kzduec920z88wfr64ylksmdcedlf5") // @manfred
Ownable = ownable.NewWithAddress("g1manfred47kzduec920z88wfr64ylksmdcedlf5") // @manfred
)

func init() {
privateLedger.Mint(owner.Owner(), 1_000_000*10_000) // @privateLedgeristrator (1M)
privateLedger.Mint(Ownable.Owner(), 1_000_000*10_000) // @privateLedgeristrator (1M)
getter := func() *grc20.Token { return Token }
grc20reg.Register(getter, "")
}
Expand Down Expand Up @@ -66,13 +66,13 @@ func Faucet() {
}

func Mint(to pusers.AddressOrName, amount uint64) {
owner.AssertCallerIsOwner()
Ownable.AssertCallerIsOwner()
toAddr := users.Resolve(to)
checkErr(privateLedger.Mint(toAddr, amount))
}

func Burn(from pusers.AddressOrName, amount uint64) {
owner.AssertCallerIsOwner()
Ownable.AssertCallerIsOwner()
fromAddr := users.Resolve(from)
checkErr(privateLedger.Burn(fromAddr, amount))
}
Expand Down
11 changes: 11 additions & 0 deletions examples/gno.land/r/demo/grc20factory/grc20factory.gno
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ func Burn(symbol string, from std.Address, amount uint64) {
checkErr(inst.ledger.Burn(from, amount))
}

// instance admin functionality
func DropInstanceOwnership(symbol string) {
inst := mustGetInstance(symbol)
checkErr(inst.admin.DropOwnership())
}

func TransferInstanceOwnership(symbol string, newOwner std.Address) {
inst := mustGetInstance(symbol)
checkErr(inst.admin.TransferOwnership(newOwner))
}

func Render(path string) string {
parts := strings.Split(path, "/")
c := len(parts)
Expand Down
26 changes: 0 additions & 26 deletions examples/gno.land/r/gnoland/events/administration.gno

This file was deleted.

10 changes: 7 additions & 3 deletions examples/gno.land/r/gnoland/events/events.gno
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"time"

"gno.land/p/demo/ownable/exts/authorizable"
"gno.land/p/demo/seqid"
"gno.land/p/demo/ufmt"
)
Expand All @@ -28,6 +29,9 @@ type (
)

var (
su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn
Auth = authorizable.NewAuthorizableWithAddress(su)

events = make(eventsSlice, 0) // sorted
idCounter seqid.ID
)
Expand All @@ -42,7 +46,7 @@ const (
// AddEvent adds auth new event
// Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00
func AddEvent(name, description, link, location, startTime, endTime string) (string, error) {
auth.AssertOnAuthList()
Auth.AssertOnAuthList()

if strings.TrimSpace(name) == "" {
return "", ErrEmptyName
Expand Down Expand Up @@ -81,7 +85,7 @@ func AddEvent(name, description, link, location, startTime, endTime string) (str

// DeleteEvent deletes an event with auth given ID
func DeleteEvent(id string) {
auth.AssertOnAuthList()
Auth.AssertOnAuthList()

e, idx, err := GetEventByID(id)
if err != nil {
Expand All @@ -99,7 +103,7 @@ func DeleteEvent(id string) {
// It only updates values corresponding to non-empty arguments sent with the call
// Note: if you need to update the start time or end time, you need to provide both every time
func EditEvent(id string, name, description, link, location, startTime, endTime string) {
auth.AssertOnAuthList()
Auth.AssertOnAuthList()

e, _, err := GetEventByID(id)
if err != nil {
Expand Down
11 changes: 3 additions & 8 deletions examples/gno.land/r/gnoland/monit/monit.gno
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var (
lastUpdate time.Time
lastCaller std.Address
wd = watchdog.Watchdog{Duration: 5 * time.Minute}
owner = ownable.New() // TODO: replace with -> ownable.NewWithAddress...
Ownable = ownable.New() // TODO: replace with -> ownable.NewWithAddress...
watchdogDuration = 5 * time.Minute
)

Expand All @@ -37,9 +37,8 @@ func Incr() int {
// Reset resets the realm state.
// This function can only be called by the admin.
func Reset() {
if owner.CallerIsOwner() != nil { // TODO: replace with owner.AssertCallerIsOwner
panic("unauthorized")
}
Ownable.AssertCallerIsOwner()

counter = 0
lastCaller = std.PrevRealm().Addr()
lastUpdate = time.Now()
Expand All @@ -53,7 +52,3 @@ func Render(_ string) string {
counter, lastUpdate, lastCaller, status,
)
}

// TransferOwnership transfers ownership to a new owner. This is a proxy to
// ownable.Ownable.TransferOwnership.
func TransferOwnership(newOwner std.Address) { owner.TransferOwnership(newOwner) }
Loading

0 comments on commit 158e545

Please sign in to comment.