From 75d79a3c025660fe82e008690acd6f69774592e5 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Thu, 4 Jul 2024 21:04:50 +0200 Subject: [PATCH 1/8] Update valopers implementation --- examples/gno.land/r/gnoland/valopers/gno.mod | 6 +- examples/gno.land/r/gnoland/valopers/init.gno | 7 + .../gno.land/r/gnoland/valopers/valopers.gno | 161 +++++++++-- .../r/gnoland/valopers/valopers_test.gno | 253 ++++++++++++++++++ 4 files changed, 404 insertions(+), 23 deletions(-) create mode 100644 examples/gno.land/r/gnoland/valopers/init.gno create mode 100644 examples/gno.land/r/gnoland/valopers/valopers_test.gno diff --git a/examples/gno.land/r/gnoland/valopers/gno.mod b/examples/gno.land/r/gnoland/valopers/gno.mod index 96299ce6e35..650ba1aa458 100644 --- a/examples/gno.land/r/gnoland/valopers/gno.mod +++ b/examples/gno.land/r/gnoland/valopers/gno.mod @@ -1,6 +1,8 @@ module gno.land/r/gnoland/valopers require ( - gno.land/p/demo/ownable v0.0.0-latest - gno.land/r/gov/dao v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/valopers/init.gno b/examples/gno.land/r/gnoland/valopers/init.gno new file mode 100644 index 00000000000..eea36fcf0ce --- /dev/null +++ b/examples/gno.land/r/gnoland/valopers/init.gno @@ -0,0 +1,7 @@ +package valopers + +import "gno.land/p/demo/avl" + +func init() { + valopers = avl.NewTree() +} diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index e35eb749c8f..a0b5f726ec5 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -5,35 +5,154 @@ package valopers import ( "std" - "gno.land/p/demo/ownable" - govdao "gno.land/r/gov/dao" + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" ) -// Valoper represents a validator operator profile. +const daoPkgPath = "gno.land/r/gov/dao" + +const ( + errValoperExists = "valoper already exists" + errValoperMissing = "valoper does not exist" + errInvalidAddressUpdate = "valoper updated address exists" + + errNotGovDAO = "caller not govdao executor" +) + +// valopers keeps track of all the active validator operators +var valopers *avl.Tree // Address -> Valoper + +// Valoper represents a validator operator profile type Valoper struct { - ownable.Ownable // Embedding the Ownable type for ownership management. + Name string // the display name of the valoper + Description string // the description of the valoper - DisplayName string // The display name of the valoper. - ValidatorAddr std.Address // The address of the validator. - // TODO: Add other valoper metadata as needed. + Address std.Address // The bech32 gno address of the validator + PubKey string // the bech32 public key of the validator + P2PAddresses []string // the publicly reachable P2P addresses of the validator } -// Register registers a new valoper. -// TODO: Define the parameters and implement the function. -func Register( /* TBD */ ) { - panic("not implemented") +// Register registers a new valoper +func Register(v Valoper) { + assertGovDAOCaller() + + // Check if the valoper is already registered + if isValoper(v.Address) { + panic(errValoperExists) + } + + // TODO add address derivation from public key + // (when the laws of gno make it possible) + + // Save the valoper to the set + valopers.Set(v.Address.String(), v) } -// Update updates an existing valoper. -// TODO: Define the parameters and implement the function. -func Update( /* TBD */ ) { - panic("not implemented") +// Unregister unregisters a valoper +func Unregister(address std.Address) { + assertGovDAOCaller() + + if !isValoper(address) { + panic(errValoperMissing) + } + + // Remove the valoper + valopers.Remove(address.String()) +} + +// Update updates an existing valoper +func Update(address std.Address, v Valoper) { + assertGovDAOCaller() + + // Check if the valoper is present + if !isValoper(address) { + panic(errValoperMissing) + } + + // Check that the valoper wouldn't be + // overwriting an existing one + isAddressUpdate := address != v.Address + if isAddressUpdate && isValoper(v.Address) { + panic(errInvalidAddressUpdate) + } + + // Remove the old valoper info + // in case the address changed + if address != v.Address { + valopers.Remove(address.String()) + } + + // Save the new valoper info + valopers.Set(v.Address.String(), v) +} + +// Fetch fetches the valoper, if present +func Fetch(address std.Address) Valoper { + valoperRaw, exists := valopers.Get(address.String()) + if !exists { + panic(errValoperMissing) + } + + return valoperRaw.(Valoper) +} + +// Render renders the current valoper set +func Render(_ string) string { + if valopers.Size() == 0 { + return "No valopers to display." + } + + output := "Valset changes to apply:\n" + valopers.Iterate("", "", func(_ string, value interface{}) bool { + valoper := value.(Valoper) + + output += renderValoper(valoper) + + return false + }) + + return output +} + +// renderValoper renders a single valoper +func renderValoper(v Valoper) string { + output := ufmt.Sprintf("## %s\n", v.Name) + output += ufmt.Sprintf("%s\n\n", v.Description) + output += ufmt.Sprintf("- Address: %s\n", v.Address.String()) + output += ufmt.Sprintf("- PubKey: %s\n", v.PubKey) + output += "- P2P Addresses: [\n" + + if len(v.P2PAddresses) == 0 { + output += "]\n" + + return output + } + + for index, addr := range v.P2PAddresses { + output += addr + + if index == len(v.P2PAddresses)-1 { + output += "]\n" + + continue + } + + output += ",\n" + } + + return output +} + +// isValoper checks if the valoper exists +func isValoper(address std.Address) bool { + _, exists := valopers.Get(address.String()) + + return exists } -// GovXXX is a placeholder for a function to interact with the governance DAO. -// TODO: Define a good API and implement it. -func GovXXX() { - // Assert that the caller is a member of the governance DAO. - govdao.AssertIsMember(std.PrevRealm().Addr()) - panic("not implemented") +// assertGovDAOCaller verifies the caller is the GovDAO executor +func assertGovDAOCaller() { + if std.PrevRealm().PkgPath() != daoPkgPath { + panic(errNotGovDAO) + } } diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/valopers_test.gno new file mode 100644 index 00000000000..75200c3b4c2 --- /dev/null +++ b/examples/gno.land/r/gnoland/valopers/valopers_test.gno @@ -0,0 +1,253 @@ +package valopers + +import ( + "testing" + + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" +) + +func TestValopers_Register(t *testing.T) { + t.Parallel() + + t.Run("not govdao caller", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + v := Valoper{} + + uassert.PanicsWithMessage(t, errNotGovDAO, func() { + Register(v) + }) + }) + + t.Run("already a valoper", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + v := Valoper{ + Address: testutils.TestAddress("valoper"), + } + + // Add the valoper + valopers.Set(v.Address.String(), v) + + uassert.PanicsWithMessage(t, errValoperExists, func() { + Register(v) + }) + }) + + t.Run("successful registration", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + v := Valoper{ + Address: testutils.TestAddress("valoper"), + Name: "new valoper", + PubKey: "pub key", + } + + uassert.NotPanics(t, func() { + Register(v) + }) + + uassert.NotPanics(t, func() { + valoper := Fetch(v.Address) + + uassert.Equal(t, v.Address, valoper.Address) + uassert.Equal(t, v.Name, valoper.Name) + uassert.Equal(t, v.PubKey, valoper.PubKey) + }) + }) +} + +func TestValopers_Unregister(t *testing.T) { + t.Parallel() + + t.Run("not govdao caller", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + // Remove the valoper + uassert.PanicsWithMessage(t, errNotGovDAO, func() { + Unregister(testutils.TestAddress("valoper")) + }) + }) + + t.Run("non-existing valoper", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + // Remove the valoper + uassert.PanicsWithMessage(t, errValoperMissing, func() { + Unregister(testutils.TestAddress("valoper")) + }) + }) + + t.Run("successful removal", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + v := Valoper{ + Address: testutils.TestAddress("valoper"), + Name: "new valoper", + PubKey: "pub key", + } + + // Add the valoper + uassert.NotPanics(t, func() { + Register(v) + }) + + // Remove the valoper + uassert.NotPanics(t, func() { + Unregister(v.Address) + }) + + // Make sure the valoper is removed + uassert.PanicsWithMessage(t, errValoperMissing, func() { + _ = Fetch(v.Address) + }) + }) +} + +func TestValopers_Update(t *testing.T) { + t.Parallel() + + t.Run("not govdao caller", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + v := Valoper{} + + // Update the valoper + uassert.PanicsWithMessage(t, errNotGovDAO, func() { + Update(v.Address, v) + }) + }) + + t.Run("non-existing valoper", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + v := Valoper{} + + // Update the valoper + uassert.PanicsWithMessage(t, errValoperMissing, func() { + Update(v.Address, v) + }) + }) + + t.Run("overwrite valoper", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + one := Valoper{ + Address: testutils.TestAddress("valoper 1"), + } + + // Add the valoper + uassert.NotPanics(t, func() { + Register(one) + }) + + initialAddress := testutils.TestAddress("valoper 2") + two := Valoper{ + Address: initialAddress, + } + + // Add the valoper + uassert.NotPanics(t, func() { + Register(two) + }) + + // Update the valoper address + // so it overlaps + two = Valoper{ + Address: one.Address, + } + + // Update the valoper + uassert.PanicsWithMessage(t, errInvalidAddressUpdate, func() { + Update(initialAddress, two) + }) + }) + + t.Run("successful update", func(t *testing.T) { + t.Parallel() + + // Clear the set for the test + valopers = avl.NewTree() + + r := std.NewCodeRealm(daoPkgPath) + std.TestSetRealm(r) + + var ( + name = "new valoper" + v = Valoper{ + Address: testutils.TestAddress("valoper"), + Name: name, + PubKey: "pub key", + } + ) + + // Add the valoper + uassert.NotPanics(t, func() { + Register(v) + }) + + // Update the valoper name + v.Name = "new name" + + // Update the valoper + uassert.NotPanics(t, func() { + Update(v.Address, v) + }) + + // Make sure the valoper is updated + uassert.NotPanics(t, func() { + valoper := Fetch(v.Address) + + uassert.Equal(t, v.Name, valoper.Name) + }) + }) +} From f04c482179c92256710b95e21f78285a2c7a4521 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:30:13 +0200 Subject: [PATCH 2/8] Remove govdao dependency --- .../gno.land/r/gnoland/valopers/valopers.gno | 17 ----- .../r/gnoland/valopers/valopers_test.gno | 62 ------------------- 2 files changed, 79 deletions(-) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index a0b5f726ec5..2d4daa34c55 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -9,14 +9,10 @@ import ( "gno.land/p/demo/ufmt" ) -const daoPkgPath = "gno.land/r/gov/dao" - const ( errValoperExists = "valoper already exists" errValoperMissing = "valoper does not exist" errInvalidAddressUpdate = "valoper updated address exists" - - errNotGovDAO = "caller not govdao executor" ) // valopers keeps track of all the active validator operators @@ -34,8 +30,6 @@ type Valoper struct { // Register registers a new valoper func Register(v Valoper) { - assertGovDAOCaller() - // Check if the valoper is already registered if isValoper(v.Address) { panic(errValoperExists) @@ -50,8 +44,6 @@ func Register(v Valoper) { // Unregister unregisters a valoper func Unregister(address std.Address) { - assertGovDAOCaller() - if !isValoper(address) { panic(errValoperMissing) } @@ -62,8 +54,6 @@ func Unregister(address std.Address) { // Update updates an existing valoper func Update(address std.Address, v Valoper) { - assertGovDAOCaller() - // Check if the valoper is present if !isValoper(address) { panic(errValoperMissing) @@ -149,10 +139,3 @@ func isValoper(address std.Address) bool { return exists } - -// assertGovDAOCaller verifies the caller is the GovDAO executor -func assertGovDAOCaller() { - if std.PrevRealm().PkgPath() != daoPkgPath { - panic(errNotGovDAO) - } -} diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/valopers_test.gno index 75200c3b4c2..6ed61b9fd86 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers_test.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers_test.gno @@ -3,8 +3,6 @@ package valopers import ( "testing" - "std" - "gno.land/p/demo/avl" "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" @@ -13,28 +11,12 @@ import ( func TestValopers_Register(t *testing.T) { t.Parallel() - t.Run("not govdao caller", func(t *testing.T) { - t.Parallel() - - // Clear the set for the test - valopers = avl.NewTree() - - v := Valoper{} - - uassert.PanicsWithMessage(t, errNotGovDAO, func() { - Register(v) - }) - }) - t.Run("already a valoper", func(t *testing.T) { t.Parallel() // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - v := Valoper{ Address: testutils.TestAddress("valoper"), } @@ -53,9 +35,6 @@ func TestValopers_Register(t *testing.T) { // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - v := Valoper{ Address: testutils.TestAddress("valoper"), Name: "new valoper", @@ -79,27 +58,12 @@ func TestValopers_Register(t *testing.T) { func TestValopers_Unregister(t *testing.T) { t.Parallel() - t.Run("not govdao caller", func(t *testing.T) { - t.Parallel() - - // Clear the set for the test - valopers = avl.NewTree() - - // Remove the valoper - uassert.PanicsWithMessage(t, errNotGovDAO, func() { - Unregister(testutils.TestAddress("valoper")) - }) - }) - t.Run("non-existing valoper", func(t *testing.T) { t.Parallel() // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - // Remove the valoper uassert.PanicsWithMessage(t, errValoperMissing, func() { Unregister(testutils.TestAddress("valoper")) @@ -112,9 +76,6 @@ func TestValopers_Unregister(t *testing.T) { // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - v := Valoper{ Address: testutils.TestAddress("valoper"), Name: "new valoper", @@ -141,29 +102,12 @@ func TestValopers_Unregister(t *testing.T) { func TestValopers_Update(t *testing.T) { t.Parallel() - t.Run("not govdao caller", func(t *testing.T) { - t.Parallel() - - // Clear the set for the test - valopers = avl.NewTree() - - v := Valoper{} - - // Update the valoper - uassert.PanicsWithMessage(t, errNotGovDAO, func() { - Update(v.Address, v) - }) - }) - t.Run("non-existing valoper", func(t *testing.T) { t.Parallel() // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - v := Valoper{} // Update the valoper @@ -178,9 +122,6 @@ func TestValopers_Update(t *testing.T) { // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - one := Valoper{ Address: testutils.TestAddress("valoper 1"), } @@ -218,9 +159,6 @@ func TestValopers_Update(t *testing.T) { // Clear the set for the test valopers = avl.NewTree() - r := std.NewCodeRealm(daoPkgPath) - std.TestSetRealm(r) - var ( name = "new valoper" v = Valoper{ From 6598ecad41ba81345073b832ae517afce58d640f Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:31:57 +0200 Subject: [PATCH 3/8] renderValoper -> valoper.Render() --- examples/gno.land/r/gnoland/valopers/valopers.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index 2d4daa34c55..07d25a8bb20 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -96,7 +96,7 @@ func Render(_ string) string { valopers.Iterate("", "", func(_ string, value interface{}) bool { valoper := value.(Valoper) - output += renderValoper(valoper) + output += valoper.Render() return false }) @@ -104,8 +104,8 @@ func Render(_ string) string { return output } -// renderValoper renders a single valoper -func renderValoper(v Valoper) string { +// Render renders a single valoper with their information +func (v Valoper) Render() string { output := ufmt.Sprintf("## %s\n", v.Name) output += ufmt.Sprintf("%s\n\n", v.Description) output += ufmt.Sprintf("- Address: %s\n", v.Address.String()) From d1db53c20ebfaa81dec15b191b2dcd6d22c63dd2 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:34:23 +0200 Subject: [PATCH 4/8] Fetch -> GetByAddr --- examples/gno.land/r/gnoland/valopers/valopers.gno | 4 ++-- examples/gno.land/r/gnoland/valopers/valopers_test.gno | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index 07d25a8bb20..db29f22b512 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -76,8 +76,8 @@ func Update(address std.Address, v Valoper) { valopers.Set(v.Address.String(), v) } -// Fetch fetches the valoper, if present -func Fetch(address std.Address) Valoper { +// GetByAddr fetches the valoper using the address, if present +func GetByAddr(address std.Address) Valoper { valoperRaw, exists := valopers.Get(address.String()) if !exists { panic(errValoperMissing) diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/valopers_test.gno index 6ed61b9fd86..891c04edddf 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers_test.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers_test.gno @@ -46,7 +46,7 @@ func TestValopers_Register(t *testing.T) { }) uassert.NotPanics(t, func() { - valoper := Fetch(v.Address) + valoper := GetByAddr(v.Address) uassert.Equal(t, v.Address, valoper.Address) uassert.Equal(t, v.Name, valoper.Name) @@ -94,7 +94,7 @@ func TestValopers_Unregister(t *testing.T) { // Make sure the valoper is removed uassert.PanicsWithMessage(t, errValoperMissing, func() { - _ = Fetch(v.Address) + _ = GetByAddr(v.Address) }) }) } @@ -183,7 +183,7 @@ func TestValopers_Update(t *testing.T) { // Make sure the valoper is updated uassert.NotPanics(t, func() { - valoper := Fetch(v.Address) + valoper := GetByAddr(v.Address) uassert.Equal(t, v.Name, valoper.Name) }) From 8fc93107cec8eb94c4947659bf94da575fdb58c7 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:36:58 +0200 Subject: [PATCH 5/8] Add disable option --- .../gno.land/r/gnoland/valopers/valopers.gno | 11 +---- .../r/gnoland/valopers/valopers_test.gno | 46 +------------------ 2 files changed, 3 insertions(+), 54 deletions(-) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index db29f22b512..e2965562a8e 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -26,6 +26,7 @@ type Valoper struct { Address std.Address // The bech32 gno address of the validator PubKey string // the bech32 public key of the validator P2PAddresses []string // the publicly reachable P2P addresses of the validator + Active bool // flag indicating if the valoper is active } // Register registers a new valoper @@ -42,16 +43,6 @@ func Register(v Valoper) { valopers.Set(v.Address.String(), v) } -// Unregister unregisters a valoper -func Unregister(address std.Address) { - if !isValoper(address) { - panic(errValoperMissing) - } - - // Remove the valoper - valopers.Remove(address.String()) -} - // Update updates an existing valoper func Update(address std.Address, v Valoper) { // Check if the valoper is present diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/valopers_test.gno index 891c04edddf..89544c46ee5 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers_test.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers_test.gno @@ -55,50 +55,6 @@ func TestValopers_Register(t *testing.T) { }) } -func TestValopers_Unregister(t *testing.T) { - t.Parallel() - - t.Run("non-existing valoper", func(t *testing.T) { - t.Parallel() - - // Clear the set for the test - valopers = avl.NewTree() - - // Remove the valoper - uassert.PanicsWithMessage(t, errValoperMissing, func() { - Unregister(testutils.TestAddress("valoper")) - }) - }) - - t.Run("successful removal", func(t *testing.T) { - t.Parallel() - - // Clear the set for the test - valopers = avl.NewTree() - - v := Valoper{ - Address: testutils.TestAddress("valoper"), - Name: "new valoper", - PubKey: "pub key", - } - - // Add the valoper - uassert.NotPanics(t, func() { - Register(v) - }) - - // Remove the valoper - uassert.NotPanics(t, func() { - Unregister(v.Address) - }) - - // Make sure the valoper is removed - uassert.PanicsWithMessage(t, errValoperMissing, func() { - _ = GetByAddr(v.Address) - }) - }) -} - func TestValopers_Update(t *testing.T) { t.Parallel() @@ -175,6 +131,7 @@ func TestValopers_Update(t *testing.T) { // Update the valoper name v.Name = "new name" + v.Active = false // Update the valoper uassert.NotPanics(t, func() { @@ -186,6 +143,7 @@ func TestValopers_Update(t *testing.T) { valoper := GetByAddr(v.Address) uassert.Equal(t, v.Name, valoper.Name) + uassert.Equal(t, v.Active, valoper.Active) }) }) } From c0fb2aa23739111d96e012f752e60a2fb05c5505 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:56:08 +0200 Subject: [PATCH 6/8] Add govdao wrapper func --- .../gno.land/r/gnoland/valopers/valopers.gno | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index e2965562a8e..ee3409271e2 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -7,12 +7,17 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" + pVals "gno.land/p/sys/validators" + govdao "gno.land/r/gov/dao" + "gno.land/r/sys/validators" ) const ( errValoperExists = "valoper already exists" errValoperMissing = "valoper does not exist" errInvalidAddressUpdate = "valoper updated address exists" + errValoperInactive = "valoper is not active" + errValoperNotCaller = "valoper is not the caller" ) // valopers keeps track of all the active validator operators @@ -130,3 +135,43 @@ func isValoper(address std.Address) bool { return exists } + +// GovDAOProposal creates a proposal to the GovDAO +// for adding the given valoper to the validator set. +// This function is meant to serve as a helper +// for generating the govdao proposal +func GovDAOProposal(address std.Address) { + valoper := GetByAddr(address) + if !valoper.Active { + panic(errValoperInactive) + } + + // Make sure the valoper is the caller + if std.GetOrigCaller() != address { + panic(errValoperInactive) + } + + changesFn := func() []pVals.Validator { + return []pVals.Validator{ + { + Address: valoper.Address, + PubKey: valoper.PubKey, + VotingPower: 10, + }, + } + } + + // Create the executor + executor := validators.NewPropExecutor(changesFn) + + // Craft the proposal comment + comment := ufmt.Sprintf( + "Proposal to add valoper %s (Address: %s; PubKey: %s) to the valset", + valoper.Name, + valoper.Address.String(), + valoper.PubKey, + ) + + // Create the govdao proposal + govdao.Propose(comment, executor) +} From 2e035f83a566f2a4ae7eb66c3a78f4cc4ccd11de Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Fri, 5 Jul 2024 16:56:23 +0200 Subject: [PATCH 7/8] Tidy mod --- examples/gno.land/r/gnoland/valopers/gno.mod | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/gno.land/r/gnoland/valopers/gno.mod b/examples/gno.land/r/gnoland/valopers/gno.mod index 650ba1aa458..2d24fb27952 100644 --- a/examples/gno.land/r/gnoland/valopers/gno.mod +++ b/examples/gno.land/r/gnoland/valopers/gno.mod @@ -5,4 +5,7 @@ require ( gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/sys/validators v0.0.0-latest + gno.land/r/gov/dao v0.0.0-latest + gno.land/r/sys/validators v0.0.0-latest ) From c54e70b3d3f6f5128d33651e0c3a82f690392eb3 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 13:56:27 +0200 Subject: [PATCH 8/8] Change voting power depending on validator state --- .../gno.land/r/gnoland/valopers/valopers.gno | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index ee3409271e2..74cec941e0d 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -16,7 +16,6 @@ const ( errValoperExists = "valoper already exists" errValoperMissing = "valoper does not exist" errInvalidAddressUpdate = "valoper updated address exists" - errValoperInactive = "valoper is not active" errValoperNotCaller = "valoper is not the caller" ) @@ -141,14 +140,19 @@ func isValoper(address std.Address) bool { // This function is meant to serve as a helper // for generating the govdao proposal func GovDAOProposal(address std.Address) { - valoper := GetByAddr(address) - if !valoper.Active { - panic(errValoperInactive) - } + var ( + valoper = GetByAddr(address) + votingPower = uint64(1) + ) // Make sure the valoper is the caller if std.GetOrigCaller() != address { - panic(errValoperInactive) + panic(errValoperNotCaller) + } + + // Determine the voting power + if !valoper.Active { + votingPower = uint64(0) } changesFn := func() []pVals.Validator { @@ -156,7 +160,7 @@ func GovDAOProposal(address std.Address) { { Address: valoper.Address, PubKey: valoper.PubKey, - VotingPower: 10, + VotingPower: votingPower, }, } }