Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: remove govdao dependency in r/gov/dao/bridge #3523

Merged
merged 26 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions examples/gno.land/r/gov/dao/bridge/bridge.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,60 @@ package bridge
import (
"std"

"gno.land/p/demo/dao"
"gno.land/p/demo/ownable"
)

const initialOwner = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @moul
const (
initialOwner = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @moul
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
loader = "gno.land/r/gov/dao/loader"
)

var b *Bridge
var (
b *Bridge
Ownable = ownable.NewWithAddress(initialOwner)
)

// Bridge is the active GovDAO
// implementation bridge
type Bridge struct {
*ownable.Ownable
leohhhn marked this conversation as resolved.
Show resolved Hide resolved

dao DAO
}

// init constructs the initial GovDAO implementation
func init() {
b = &Bridge{
Ownable: ownable.NewWithAddress(initialOwner),
dao: &govdaoV2{},
dao: nil, // initially set via r/gov/dao/loader
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
}
}

// LoadGovDAO loads the initial version of GovDAO into the bridge
// All changes to b.dao need to be done via GovDAO proposals after
func LoadGovDAO(d DAO) {
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
if std.PrevRealm().PkgPath() != loader {
panic("unauthorized")
}

b.dao = d
}

// SetDAO sets the currently active GovDAO implementation
func SetDAO(dao DAO) {
b.AssertCallerIsOwner()
// NewGovDAOImplChangeExecutor allows creating a GovDAO proposal
// Which will upgrade the GovDAO version inside the bridge
func NewGovDAOImplChangeExecutor(newImpl DAO) dao.Executor {
callback := func() error {
b.dao = newImpl
return nil
}

return b.dao.NewGovDAOExecutor(callback)
}

b.dao = dao
// SetGovDAO allows the admin to set the GovDAO version manually
// This functionality can be fully disabled by Ownable.DropOwnership(),
// making this realm fully managed by GovDAO.
func SetGovDAO(d DAO) {
Ownable.AssertCallerIsOwner()
b.dao = d
}

// GovDAO returns the current GovDAO implementation
Expand Down
45 changes: 40 additions & 5 deletions examples/gno.land/r/gov/dao/bridge/bridge_test.gno
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package bridge

import (
"testing"

"std"
"testing"

"gno.land/p/demo/dao"
"gno.land/p/demo/ownable"
Expand All @@ -27,11 +26,47 @@ func TestBridge_DAO(t *testing.T) {
uassert.Equal(t, proposalID, GovDAO().Propose(dao.ProposalRequest{}))
}

func TestBridge_LoadGovDAO(t *testing.T) {
t.Run("invalid loader path", func(t *testing.T) {
std.TestSetRealm(std.NewCodeRealm("gno.land/r/demo/loader")) // invalid loader

// Attempt to set a new DAO implementation
uassert.PanicsWithMessage(t, "unauthorized", func() {
LoadGovDAO(&mockDAO{})
})
})

t.Run("valid loader", func(t *testing.T) {
var (
loader = "gno.land/r/gov/dao/loader"
proposalID = uint64(10)
mockDAO = &mockDAO{
proposeFn: func(_ dao.ProposalRequest) uint64 {
return proposalID
},
}
)

std.TestSetRealm(std.NewCodeRealm(loader))

// Attempt to set a new DAO implementation
uassert.NotPanics(t, func() {
LoadGovDAO(mockDAO)
})

uassert.Equal(
t,
mockDAO.Propose(dao.ProposalRequest{}),
GovDAO().Propose(dao.ProposalRequest{}),
)
})
}

func TestBridge_SetDAO(t *testing.T) {
t.Run("invalid owner", func(t *testing.T) {
// Attempt to set a new DAO implementation
uassert.PanicsWithMessage(t, ownable.ErrUnauthorized.Error(), func() {
SetDAO(&mockDAO{})
SetGovDAO(&mockDAO{})
})
})

Expand All @@ -49,10 +84,10 @@ func TestBridge_SetDAO(t *testing.T) {

std.TestSetOrigCaller(addr)

b.Ownable = ownable.NewWithAddress(addr)
Ownable = ownable.NewWithAddress(addr)

urequire.NotPanics(t, func() {
SetDAO(mockDAO)
SetGovDAO(mockDAO)
})

uassert.Equal(
Expand Down
42 changes: 0 additions & 42 deletions examples/gno.land/r/gov/dao/bridge/v2.gno

This file was deleted.

1 change: 1 addition & 0 deletions examples/gno.land/r/gov/dao/loader/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/gov/dao/loader
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 13 additions & 0 deletions examples/gno.land/r/gov/dao/loader/loader.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Package loader's only task is to load the initial GovDAO version into the bridge.
// This is done to avoid gov/dao/v2 as a bridge dependency,
// As this can often lead to cyclic dependency errors.
package loader

import (
"gno.land/r/gov/dao/bridge"
govdao "gno.land/r/gov/dao/v2"
)

func init() {
bridge.LoadGovDAO(govdao.GovDAO)
}
19 changes: 14 additions & 5 deletions examples/gno.land/r/gov/dao/v2/dao.gno
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ import (
var (
d *simpledao.SimpleDAO // the current active DAO implementation
members membstore.MemberStore // the member store

// GovDAO exposes all functions of this contract as methods
GovDAO = &DAO{}
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
)

// DAO is an empty struct that allows all
// functions of this realm to be methods instead of functions
// This allows a registry, such as r/gov/dao/bridge
// to take this object and match it to a required interface
type DAO struct{}

const daoPkgPath = "gno.land/r/gov/dao/v2"

func init() {
Expand All @@ -33,7 +42,7 @@ func init() {

// Propose is designed to be called by another contract or with
// `maketx run`, not by a `maketx call`.
func Propose(request dao.ProposalRequest) uint64 {
func (_ DAO) Propose(request dao.ProposalRequest) uint64 {
leohhhn marked this conversation as resolved.
Show resolved Hide resolved
idx, err := d.Propose(request)
if err != nil {
panic(err)
Expand All @@ -43,25 +52,25 @@ func Propose(request dao.ProposalRequest) uint64 {
}

// VoteOnProposal casts a vote for the given proposal
func VoteOnProposal(id uint64, option dao.VoteOption) {
func (_ DAO) VoteOnProposal(id uint64, option dao.VoteOption) {
if err := d.VoteOnProposal(id, option); err != nil {
panic(err)
}
}

// ExecuteProposal executes the proposal
func ExecuteProposal(id uint64) {
func (_ DAO) ExecuteProposal(id uint64) {
if err := d.ExecuteProposal(id); err != nil {
panic(err)
}
}

// GetPropStore returns the active proposal store
func GetPropStore() dao.PropStore {
func (_ DAO) GetPropStore() dao.PropStore {
return d
}

// GetMembStore returns the active member store
func GetMembStore() membstore.MemberStore {
func (_ DAO) GetMembStore() membstore.MemberStore {
return members
}
10 changes: 5 additions & 5 deletions examples/gno.land/r/gov/dao/v2/poc.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
var errNoChangesProposed = errors.New("no set changes proposed")

// NewGovDAOExecutor creates the govdao wrapped callback executor
func NewGovDAOExecutor(cb func() error) dao.Executor {
func (_ DAO) NewGovDAOExecutor(cb func() error) dao.Executor {
if cb == nil {
panic(errNoChangesProposed)
}
Expand All @@ -25,7 +25,7 @@ func NewGovDAOExecutor(cb func() error) dao.Executor {
}

// NewMemberPropExecutor returns the GOVDAO member change executor
func NewMemberPropExecutor(changesFn func() []membstore.Member) dao.Executor {
func (_ DAO) NewMemberPropExecutor(changesFn func() []membstore.Member) dao.Executor {
if changesFn == nil {
panic(errNoChangesProposed)
}
Expand Down Expand Up @@ -65,10 +65,10 @@ func NewMemberPropExecutor(changesFn func() []membstore.Member) dao.Executor {
return errs
}

return NewGovDAOExecutor(callback)
return GovDAO.NewGovDAOExecutor(callback)
}

func NewMembStoreImplExecutor(changeFn func() membstore.MemberStore) dao.Executor {
func (_ DAO) NewMembStoreImplExecutor(changeFn func() membstore.MemberStore) dao.Executor {
if changeFn == nil {
panic(errNoChangesProposed)
}
Expand All @@ -79,7 +79,7 @@ func NewMembStoreImplExecutor(changeFn func() membstore.MemberStore) dao.Executo
return nil
}

return NewGovDAOExecutor(callback)
return GovDAO.NewGovDAOExecutor(callback)
}

// setMembStoreImpl sets a new dao.MembStore implementation
Expand Down
8 changes: 5 additions & 3 deletions examples/gno.land/r/gov/dao/v2/prop1_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (

"gno.land/p/demo/dao"
pVals "gno.land/p/sys/validators"
_ "gno.land/r/gov/dao/loader" // so that loader.init is executed
govdao "gno.land/r/gov/dao/v2"
validators "gno.land/r/sys/validators/v2"
)

func init() {

changesFn := func() []pVals.Validator {
return []pVals.Validator{
{
Expand Down Expand Up @@ -51,7 +53,7 @@ func init() {
Executor: executor,
}

govdao.Propose(prop)
govdao.GovDAO.Propose(prop)
}

func main() {
Expand All @@ -60,13 +62,13 @@ func main() {
println("--")
println(govdao.Render("0"))
println("--")
govdao.VoteOnProposal(0, dao.YesVote)
govdao.GovDAO.VoteOnProposal(0, dao.YesVote)
println("--")
println(govdao.Render("0"))
println("--")
println(validators.Render(""))
println("--")
govdao.ExecuteProposal(0)
govdao.GovDAO.ExecuteProposal(0)
println("--")
println(govdao.Render("0"))
println("--")
Expand Down
7 changes: 4 additions & 3 deletions examples/gno.land/r/gov/dao/v2/prop2_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"gno.land/p/demo/dao"
gnoblog "gno.land/r/gnoland/blog"
_ "gno.land/r/gov/dao/loader" // so that loader.init is executed
govdao "gno.land/r/gov/dao/v2"
)

Expand All @@ -28,7 +29,7 @@ func init() {
Executor: ex,
}

govdao.Propose(prop)
govdao.GovDAO.Propose(prop)
}

func main() {
Expand All @@ -37,13 +38,13 @@ func main() {
println("--")
println(govdao.Render("0"))
println("--")
govdao.VoteOnProposal(0, "YES")
govdao.GovDAO.VoteOnProposal(0, "YES")
println("--")
println(govdao.Render("0"))
println("--")
println(gnoblog.Render(""))
println("--")
govdao.ExecuteProposal(0)
govdao.GovDAO.ExecuteProposal(0)
println("--")
println(govdao.Render("0"))
println("--")
Expand Down
Loading
Loading