From 277f1712627aa056b723594a5103b303abe7a666 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 29 Jan 2025 00:42:19 +0100 Subject: [PATCH 1/4] modify pausable --- .../gno.land/p/demo/pausable/pausable.gno | 27 +++++++++---------- .../p/demo/pausable/pausable_test.gno | 27 +++++-------------- 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index e6a85771fa6..9e52e164014 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -7,23 +7,15 @@ import ( ) type Pausable struct { - *ownable.Ownable + o *ownable.Ownable paused bool } -// New returns a new Pausable struct with non-paused state as default -func New() *Pausable { - return &Pausable{ - Ownable: ownable.New(), - paused: false, - } -} - // NewFromOwnable is the same as New, but with a pre-existing top-level ownable func NewFromOwnable(ownable *ownable.Ownable) *Pausable { return &Pausable{ - Ownable: ownable, - paused: false, + o: ownable, + paused: false, } } @@ -34,24 +26,29 @@ 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 !p.CallerIsOwner() { + if !p.o.CallerIsOwner() { return ownable.ErrUnauthorized } p.paused = true - std.Emit("Paused", "account", p.Owner().String()) + std.Emit("Paused", "account", p.o.Owner().String()) return nil } // Unpause sets the state of Pausable to false, meaning all pausable functions are resumed func (p *Pausable) Unpause() error { - if !p.CallerIsOwner() { + if !p.o.CallerIsOwner() { return ownable.ErrUnauthorized } p.paused = false - std.Emit("Unpaused", "account", p.Owner().String()) + std.Emit("Unpaused", "account", p.o.Owner().String()) return nil } + +// Owner returns the underlying owner of the pausable object +func (p *Pausable) Owner() std.Address { + return p.o.Owner() +} diff --git a/examples/gno.land/p/demo/pausable/pausable_test.gno b/examples/gno.land/p/demo/pausable/pausable_test.gno index c9557245bdf..1d837b77770 100644 --- a/examples/gno.land/p/demo/pausable/pausable_test.gno +++ b/examples/gno.land/p/demo/pausable/pausable_test.gno @@ -9,53 +9,38 @@ import ( ) var ( - firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de") - secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa") + firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de") + o = ownable.NewWithAddress(firstCaller) ) -func TestNew(t *testing.T) { - std.TestSetOrigCaller(firstCaller) - - result := New() - - urequire.False(t, result.paused, "Expected result to be unpaused") - urequire.Equal(t, firstCaller.String(), result.Owner().String()) -} - func TestNewFromOwnable(t *testing.T) { std.TestSetOrigCaller(firstCaller) - o := ownable.New() - std.TestSetOrigCaller(secondCaller) result := NewFromOwnable(o) - urequire.Equal(t, firstCaller.String(), result.Owner().String()) } func TestSetUnpaused(t *testing.T) { std.TestSetOrigCaller(firstCaller) + result := NewFromOwnable(o) - result := New() result.Unpause() - urequire.False(t, result.IsPaused(), "Expected result to be unpaused") } func TestSetPaused(t *testing.T) { std.TestSetOrigCaller(firstCaller) + result := NewFromOwnable(o) - result := New() result.Pause() - urequire.True(t, result.IsPaused(), "Expected result to be paused") } func TestIsPaused(t *testing.T) { - std.TestSetOrigCaller(firstCaller) - - result := New() + result := NewFromOwnable(o) urequire.False(t, result.IsPaused(), "Expected result to be unpaused") + std.TestSetOrigCaller(firstCaller) result.Pause() urequire.True(t, result.IsPaused(), "Expected result to be paused") } From 45077eb83b6886bb659a595c540513fad8bb8eaf Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 29 Jan 2025 10:54:29 +0100 Subject: [PATCH 2/4] ownable --- examples/gno.land/p/demo/pausable/pausable.gno | 6 +++--- .../gno.land/p/demo/pausable/pausable_test.gno | 15 +++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index 9e52e164014..fa3962cab41 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -48,7 +48,7 @@ func (p *Pausable) Unpause() error { return nil } -// Owner returns the underlying owner of the pausable object -func (p *Pausable) Owner() std.Address { - return p.o.Owner() +// Ownable returns the underlying ownable +func (p *Pausable) Ownable() *ownable.Ownable { + return p.o } diff --git a/examples/gno.land/p/demo/pausable/pausable_test.gno b/examples/gno.land/p/demo/pausable/pausable_test.gno index 1d837b77770..47028cd85c8 100644 --- a/examples/gno.land/p/demo/pausable/pausable_test.gno +++ b/examples/gno.land/p/demo/pausable/pausable_test.gno @@ -5,6 +5,7 @@ import ( "testing" "gno.land/p/demo/ownable" + "gno.land/p/demo/uassert" "gno.land/p/demo/urequire" ) @@ -17,7 +18,7 @@ func TestNewFromOwnable(t *testing.T) { std.TestSetOrigCaller(firstCaller) result := NewFromOwnable(o) - urequire.Equal(t, firstCaller.String(), result.Owner().String()) + urequire.Equal(t, firstCaller.String(), result.Ownable().Owner().String()) } func TestSetUnpaused(t *testing.T) { @@ -25,7 +26,7 @@ func TestSetUnpaused(t *testing.T) { result := NewFromOwnable(o) result.Unpause() - urequire.False(t, result.IsPaused(), "Expected result to be unpaused") + uassert.False(t, result.IsPaused(), "Expected result to be unpaused") } func TestSetPaused(t *testing.T) { @@ -33,7 +34,7 @@ func TestSetPaused(t *testing.T) { result := NewFromOwnable(o) result.Pause() - urequire.True(t, result.IsPaused(), "Expected result to be paused") + uassert.True(t, result.IsPaused(), "Expected result to be paused") } func TestIsPaused(t *testing.T) { @@ -42,5 +43,11 @@ func TestIsPaused(t *testing.T) { std.TestSetOrigCaller(firstCaller) result.Pause() - urequire.True(t, result.IsPaused(), "Expected result to be paused") + uassert.True(t, result.IsPaused(), "Expected result to be paused") +} + +func TestOwnable(t *testing.T) { + result := NewFromOwnable(o) + + uassert.Equal(t, result.Ownable().Owner(), o.Owner()) } From ea58f549c67c08cba5ae40d22333b872d46967a5 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:04:43 +0100 Subject: [PATCH 3/4] chore: make ownable working with not fully initialized Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/ownable/ownable.gno | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index f565e27c0f2..a8cb5ea95a7 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -65,18 +65,28 @@ func (o *Ownable) DropOwnership() error { } // Owner returns the owner address from Ownable -func (o Ownable) Owner() std.Address { +func (o *Ownable) Owner() std.Address { + if o == nil { + return std.Address("") + } return o.owner } // CallerIsOwner checks if the caller of the function is the Realm's owner -func (o Ownable) CallerIsOwner() bool { +func (o *Ownable) CallerIsOwner() bool { + if o == nil { + return false + } 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 { +func (o *Ownable) AssertCallerIsOwner() { + if o == nil { + panic(ErrUnauthorized) + } + caller := std.PrevRealm().Addr() + if caller != o.owner { panic(ErrUnauthorized) } } From e54e26969620767624e738e8910749e50693251c Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:17:37 +0100 Subject: [PATCH 4/4] test: add tests for Ownable --- .../gno.land/p/demo/ownable/ownable_test.gno | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno index f58af9642c6..d8b7f9a8e3a 100644 --- a/examples/gno.land/p/demo/ownable/ownable_test.gno +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -93,3 +93,51 @@ func TestErrInvalidAddress(t *testing.T) { err = o.TransferOwnership("10000000001000000000100000000010000000001000000000") uassert.ErrorContains(t, err, ErrInvalidAddress.Error()) } + +func TestAssertCallerIsOwner(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + o := New() + + // Should not panic when caller is owner + o.AssertCallerIsOwner() + + // Should panic when caller is not owner + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + defer func() { + r := recover() + if r == nil { + t.Error("expected panic but got none") + } + if r != ErrUnauthorized { + t.Errorf("expected ErrUnauthorized but got %v", r) + } + }() + o.AssertCallerIsOwner() +} + +func TestNilReceiver(t *testing.T) { + var o *Ownable + + owner := o.Owner() + if owner != std.Address("") { + t.Errorf("expected empty address but got %v", owner) + } + + isOwner := o.CallerIsOwner() + uassert.False(t, isOwner) + + defer func() { + r := recover() + if r == nil { + t.Error("expected panic but got none") + } + if r != ErrUnauthorized { + t.Errorf("expected ErrUnauthorized but got %v", r) + } + }() + o.AssertCallerIsOwner() +}