Skip to content

Commit

Permalink
Merge branch 'master' into gnolang-add-build-constraint-to-panic-on-3…
Browse files Browse the repository at this point in the history
…2-bit-architectures
  • Loading branch information
odeke-em authored Jan 31, 2025
2 parents 725e608 + d3774ce commit b411964
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/gnoland.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ on:
# Changes to examples/ can create failures in gno.land, eg. txtars,
# see: https://github.com/gnolang/gno/pull/3590
- examples/**
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/gnovm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
paths:
- gnovm/**
- tm2/** # GnoVM has a dependency on TM2 types
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tm2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:
pull_request:
paths:
- tm2/**
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
10 changes: 10 additions & 0 deletions examples/gno.land/p/oxtekgrinder/ownable2step/errors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ownable2step

import "errors"

var (
ErrNoPendingOwner = errors.New("ownable2step: no pending owner")
ErrUnauthorized = errors.New("ownable2step: caller is not owner")
ErrPendingUnauthorized = errors.New("ownable2step: caller is not pending owner")
ErrInvalidAddress = errors.New("ownable2step: new owner address is invalid")
)
1 change: 1 addition & 0 deletions examples/gno.land/p/oxtekgrinder/ownable2step/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/oxtekgrinder/ownable2step
98 changes: 98 additions & 0 deletions examples/gno.land/p/oxtekgrinder/ownable2step/ownable.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package ownable2step

import (
"std"
)

const OwnershipTransferEvent = "OwnershipTransfer"

// Ownable2Step is a two-step ownership transfer package
// It allows the current owner to set a new owner and the new owner will need to accept the ownership before it is transferred
type Ownable2Step struct {
owner std.Address
pendingOwner std.Address
}

func New() *Ownable2Step {
return &Ownable2Step{
owner: std.PrevRealm().Addr(),
pendingOwner: "",
}
}

func NewWithAddress(addr std.Address) *Ownable2Step {
return &Ownable2Step{
owner: addr,
pendingOwner: "",
}
}

// TransferOwnership initiate the transfer of the ownership to a new address by setting the PendingOwner
func (o *Ownable2Step) TransferOwnership(newOwner std.Address) error {
if !o.CallerIsOwner() {
return ErrUnauthorized
}
if !newOwner.IsValid() {
return ErrInvalidAddress
}

o.pendingOwner = newOwner
return nil
}

// AcceptOwnership accepts the pending ownership transfer
func (o *Ownable2Step) AcceptOwnership() error {
if o.pendingOwner.String() == "" {
return ErrNoPendingOwner
}
if std.PrevRealm().Addr() != o.pendingOwner {
return ErrPendingUnauthorized
}

o.owner = o.pendingOwner
o.pendingOwner = ""

return nil
}

// DropOwnership removes the owner, effectively disabling any owner-related actions
// Top-level usage: disables all only-owner actions/functions,
// Embedded usage: behaves like a burn functionality, removing the owner from the struct
func (o *Ownable2Step) DropOwnership() error {
if !o.CallerIsOwner() {
return ErrUnauthorized
}

prevOwner := o.owner
o.owner = ""

std.Emit(
OwnershipTransferEvent,
"from", prevOwner.String(),
"to", "",
)

return nil
}

// Owner returns the owner address from Ownable
func (o *Ownable2Step) Owner() std.Address {
return o.owner
}

// PendingOwner returns the pending owner address from Ownable2Step
func (o *Ownable2Step) PendingOwner() std.Address {
return o.pendingOwner
}

// CallerIsOwner checks if the caller of the function is the Realm's owner
func (o *Ownable2Step) CallerIsOwner() bool {
return std.PrevRealm().Addr() == o.owner
}

// AssertCallerIsOwner panics if the caller is not the owner
func (o *Ownable2Step) AssertCallerIsOwner() {
if std.PrevRealm().Addr() != o.owner {
panic(ErrUnauthorized)
}
}
156 changes: 156 additions & 0 deletions examples/gno.land/p/oxtekgrinder/ownable2step/ownable_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package ownable2step

import (
"std"
"testing"

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

var (
alice = testutils.TestAddress("alice")
bob = testutils.TestAddress("bob")
)

func TestNew(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))
std.TestSetOrigCaller(alice)

o := New()
got := o.Owner()
pendingOwner := o.PendingOwner()

uassert.Equal(t, got, alice)
uassert.Equal(t, pendingOwner.String(), "")
}

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

got := o.Owner()
pendingOwner := o.PendingOwner()

uassert.Equal(t, got, alice)
uassert.Equal(t, pendingOwner.String(), "")
}

func TestInitiateTransferOwnership(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))
std.TestSetOrigCaller(alice)

o := New()

err := o.TransferOwnership(bob)
urequire.NoError(t, err)

owner := o.Owner()
pendingOwner := o.PendingOwner()

uassert.Equal(t, owner, alice)
uassert.Equal(t, pendingOwner, bob)
}

func TestTransferOwnership(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))
std.TestSetOrigCaller(alice)

o := New()

err := o.TransferOwnership(bob)
urequire.NoError(t, err)

owner := o.Owner()
pendingOwner := o.PendingOwner()

uassert.Equal(t, owner, alice)
uassert.Equal(t, pendingOwner, bob)

std.TestSetRealm(std.NewUserRealm(bob))
std.TestSetOrigCaller(bob)

err = o.AcceptOwnership()
urequire.NoError(t, err)

owner = o.Owner()
pendingOwner = o.PendingOwner()

uassert.Equal(t, owner, bob)
uassert.Equal(t, pendingOwner.String(), "")
}

func TestCallerIsOwner(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))
std.TestSetOrigCaller(alice)

o := New()
unauthorizedCaller := bob

std.TestSetRealm(std.NewUserRealm(unauthorizedCaller))
std.TestSetOrigCaller(unauthorizedCaller)

uassert.False(t, o.CallerIsOwner())
}

func TestDropOwnership(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))

o := New()

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

owner := o.Owner()
uassert.Empty(t, owner, "owner should be empty")
}

// Errors

func TestErrUnauthorized(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))
std.TestSetOrigCaller(alice)

o := New()

std.TestSetRealm(std.NewUserRealm(bob))
std.TestSetOrigCaller(bob)

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

func TestErrInvalidAddress(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))

o := New()

err := o.TransferOwnership("")
uassert.ErrorContains(t, err, ErrInvalidAddress.Error())

err = o.TransferOwnership("10000000001000000000100000000010000000001000000000")
uassert.ErrorContains(t, err, ErrInvalidAddress.Error())
}

func TestErrNoPendingOwner(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))

o := New()

err := o.AcceptOwnership()
uassert.ErrorContains(t, err, ErrNoPendingOwner.Error())
}

func TestErrPendingUnauthorized(t *testing.T) {
std.TestSetRealm(std.NewUserRealm(alice))

o := New()

err := o.TransferOwnership(bob)
urequire.NoError(t, err)

std.TestSetRealm(std.NewUserRealm(alice))

err = o.AcceptOwnership()
uassert.ErrorContains(t, err, ErrPendingUnauthorized.Error())
}

0 comments on commit b411964

Please sign in to comment.