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

feat: improve r/gnoland/valopers implementation #2509

Merged
merged 11 commits into from
Jul 6, 2024
6 changes: 4 additions & 2 deletions examples/gno.land/r/gnoland/valopers/gno.mod
Original file line number Diff line number Diff line change
@@ -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
)
7 changes: 7 additions & 0 deletions examples/gno.land/r/gnoland/valopers/init.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package valopers

import "gno.land/p/demo/avl"

func init() {
valopers = avl.NewTree()
}
161 changes: 140 additions & 21 deletions examples/gno.land/r/gnoland/valopers/valopers.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
moul marked this conversation as resolved.
Show resolved Hide resolved
assertGovDAOCaller()
moul marked this conversation as resolved.
Show resolved Hide resolved

// 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)
moul marked this conversation as resolved.
Show resolved Hide resolved

// 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) {
moul marked this conversation as resolved.
Show resolved Hide resolved
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()
moul marked this conversation as resolved.
Show resolved Hide resolved

// 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 {
moul marked this conversation as resolved.
Show resolved Hide resolved
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 {
moul marked this conversation as resolved.
Show resolved Hide resolved
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() {
moul marked this conversation as resolved.
Show resolved Hide resolved
// 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)
}
}
Loading
Loading