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: align on-chain valset with genesis.json valset #2465

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c4ef46b
Add the initial /sys/vals PoS implementation
zivkovicmilos May 16, 2024
8a6ea5c
Fix imports
zivkovicmilos May 17, 2024
e53e6bf
Add gno.mods, add initial PoA
zivkovicmilos May 17, 2024
9a8bc1d
Simplify API
zivkovicmilos May 17, 2024
b4a2452
Add initial PoS add validator tests
zivkovicmilos May 21, 2024
1c6a775
Add additional PoS tests
zivkovicmilos May 21, 2024
54884fd
Add initial PoS remove validator tests
zivkovicmilos May 21, 2024
afc2fcc
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos May 24, 2024
d2e1e24
Add unit tests for PoS validator removal
zivkovicmilos May 24, 2024
08f1351
Add unit tests for PoA validator operations
zivkovicmilos May 24, 2024
ee678fe
Cleanup
zivkovicmilos May 24, 2024
5a9385d
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos May 24, 2024
fe8ac45
Cleanup
zivkovicmilos May 24, 2024
4c21012
Fix stdshim std.Emit
zivkovicmilos May 24, 2024
6a76e48
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 3, 2024
997f6af
Apply suggestions for set scraping
zivkovicmilos Jun 3, 2024
2e5aca1
Merge master; adapt r/sys/vals
zivkovicmilos Jun 17, 2024
6e61bf8
Add PoC
zivkovicmilos Jun 17, 2024
28abe9b
Drop JSON output for valsets
zivkovicmilos Jun 17, 2024
26325c2
Cleanup
zivkovicmilos Jun 17, 2024
eb34c7a
Tidy gno mod
zivkovicmilos Jun 17, 2024
a3049c3
Move back to r/sys/vals
zivkovicmilos Jun 17, 2024
cfd900d
Tidy gno mod
zivkovicmilos Jun 17, 2024
f1151be
Tidy
zivkovicmilos Jun 18, 2024
32fc706
Tidy
zivkovicmilos Jun 18, 2024
2be7527
Move testing helpers to uassert
zivkovicmilos Jun 18, 2024
ce9329a
Add the 'testing' package to the whitelist
zivkovicmilos Jun 19, 2024
b388ef4
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 20, 2024
2c85e06
Revert "Move testing helpers to uassert"
zivkovicmilos Jun 20, 2024
40cf5bb
Tidy go mods
zivkovicmilos Jun 20, 2024
5562b5b
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 21, 2024
a28623f
Drop PoA / PoS in this scope
zivkovicmilos Jun 26, 2024
2d365dd
Temporary rename
zivkovicmilos Jun 26, 2024
d1e3c96
Temporary rename
zivkovicmilos Jun 26, 2024
32ec0b6
Switch to testutils
zivkovicmilos Jun 26, 2024
d213e89
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 26, 2024
d81d300
Add voting power to AddValidator API
zivkovicmilos Jun 26, 2024
68534e7
Add GetValidator to Protocol API
zivkovicmilos Jun 26, 2024
51414b0
Switch to avl.Tree for map in PoC
zivkovicmilos Jun 26, 2024
53de738
Rename Protocol -> ValsetProtocol
zivkovicmilos Jun 26, 2024
8b6130a
Temporary rename
zivkovicmilos Jun 26, 2024
0860453
Temporary rename
zivkovicmilos Jun 26, 2024
68a2723
Temporary rename
zivkovicmilos Jun 26, 2024
ed2f7a6
Update examples/gno.land/r/sys/vals/vals.gno
zivkovicmilos Jun 27, 2024
3a7fd9c
Simplify verify methods
zivkovicmilos Jun 27, 2024
751b5d3
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 28, 2024
6a5978f
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jun 28, 2024
8699bfe
Add genesis.json and on-chain valset alignment
zivkovicmilos Jun 30, 2024
41cbb2e
Use constant in cfg
zivkovicmilos Jun 30, 2024
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
8 changes: 8 additions & 0 deletions examples/gno.land/p/sys/vals/poc/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module gno.land/p/sys/vals/poc

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/p/sys/vals/types v0.0.0-latest
)
15 changes: 15 additions & 0 deletions examples/gno.land/p/sys/vals/poc/option.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package poc

import "gno.land/p/sys/vals/types"

type Option func(*PoC)

// WithInitialSet sets the initial PoA validator set
func WithInitialSet(validators []*types.Validator) Option {
return func(p *PoC) {
for index, validator := range validators {
p.validators = append(p.validators, validator)
p.addressToValidatorIndex.Set(validator.Address.String(), index)
}
}
}
103 changes: 103 additions & 0 deletions examples/gno.land/p/sys/vals/poc/poc.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package poc

import (
"std"

"gno.land/p/demo/avl"
"gno.land/p/sys/vals/types"
)

const errInvalidVotingPower = "invalid voting power"

// PoC specifies the Proof of Contribution validator set.
// In order to become part of the set, users to be voted into
// the validator set by a govdao proposal
type PoC struct {
// validators holds the current validator set.
// This slice can never practically grow more than ~150 elements,
// due to Tendermint's quadratic network complexity
validators []*types.Validator
addressToValidatorIndex *avl.Tree // address -> index
}

// NewPoC creates a new empty Proof of Contribution validator set
func NewPoC(opts ...Option) *PoC {
// Create the empty set
p := &PoC{
validators: make([]*types.Validator, 0),
addressToValidatorIndex: avl.NewTree(),
}

// Apply the options
for _, opt := range opts {
opt(p)
}

return p
}

func (p *PoC) AddValidator(address std.Address, pubKey string, power uint64) *types.Validator {
// Validate that the operation is a valid call
// Check if the validator is already in the set
if p.IsValidator(address) {
panic(types.ErrValidatorExists)
}

// Make sure the voting power > 0
if power == 0 {
panic(errInvalidVotingPower)
}

v := &types.Validator{
Address: address,
PubKey: pubKey, // TODO: in the future, verify the public key
VotingPower: power,
}

// Add the validator to the set
p.addressToValidatorIndex.Set(v.Address.String(), len(p.validators))
p.validators = append(p.validators, v)

return v
}

func (p *PoC) RemoveValidator(address std.Address) *types.Validator {
// Validate that the operation is a valid call
// Check if the address is a validator
if !p.IsValidator(address) {
panic(types.ErrValidatorMissing)
}

addressStr := address.String()

// Fetch the validator index
indexRaw, _ := p.addressToValidatorIndex.Get(addressStr)
index := indexRaw.(int)

// Remove the validator from the set
validator := p.validators[index]
p.validators = append(p.validators[:index], p.validators[index+1:]...)

p.addressToValidatorIndex.Remove(addressStr)

return validator
}

func (p *PoC) IsValidator(address std.Address) bool {
_, exists := p.addressToValidatorIndex.Get(address.String())

return exists
}

func (p *PoC) GetValidator(address std.Address) types.Validator {
valIndexRaw, exists := p.addressToValidatorIndex.Get(address.String())
if !exists {
panic(types.ErrValidatorMissing)
}

return *p.validators[valIndexRaw.(int)]
}

func (p *PoC) GetValidators() []*types.Validator {
return p.validators
}
192 changes: 192 additions & 0 deletions examples/gno.land/p/sys/vals/poc/poc_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package poc

import (
"testing"

"gno.land/p/demo/testutils"

"gno.land/p/demo/ufmt"
"gno.land/p/sys/vals/types"
)

// generateTestValidators generates a dummy validator set
func generateTestValidators(count int) []*types.Validator {
vals := make([]*types.Validator, 0, count)

for i := 0; i < count; i++ {
val := &types.Validator{
Address: testutils.TestAddress(ufmt.Sprintf("%d", i)),
PubKey: "public-key",
VotingPower: 1,
}

vals = append(vals, val)
}

return vals
}

func TestPoC_AddValidator_Invalid(t *testing.T) {
t.Parallel()

t.Run("validator already in set", func(t *testing.T) {
t.Parallel()

var (
proposalAddress = testutils.TestAddress("caller")
proposalKey = "public-key"

initialSet = generateTestValidators(1)
)

initialSet[0].Address = proposalAddress
initialSet[0].PubKey = proposalKey

// Create the protocol with an initial set
p := NewPoC(WithInitialSet(initialSet))

// Attempt to add the validator
testing.PanicsWithError(t, types.ErrValidatorExists, func() {
p.AddValidator(proposalAddress, proposalKey, 1)
})
})

t.Run("invalid voting power", func(t *testing.T) {
t.Parallel()

var (
proposalAddress = testutils.TestAddress("caller")
proposalKey = "public-key"
)

// Create the protocol with no initial set
p := NewPoC()

// Attempt to add the validator
testing.PanicsWithError(t, errInvalidVotingPower, func() {
p.AddValidator(proposalAddress, proposalKey, 0)
})
})
}

func TestPoC_AddValidator(t *testing.T) {
t.Parallel()

var (
proposalAddress = testutils.TestAddress("caller")
proposalKey = "public-key"
)

// Create the protocol with no initial set
p := NewPoC()

// Attempt to add the validator
testing.NotPanics(t, func() {
p.AddValidator(proposalAddress, proposalKey, 1)
})

// Make sure the validator is added
if !p.IsValidator(proposalAddress) || len(p.validators) != 1 {
t.Fatal("address is not validator")
}
}

func TestPoC_RemoveValidator_Invalid(t *testing.T) {
t.Parallel()

t.Run("proposed removal not in set", func(t *testing.T) {
t.Parallel()

var (
proposalAddress = testutils.TestAddress("caller")
initialSet = generateTestValidators(1)
)

initialSet[0].Address = proposalAddress

// Create the protocol with an initial set
p := NewPoC(WithInitialSet(initialSet))

// Attempt to remove the validator
testing.PanicsWithError(t, types.ErrValidatorMissing, func() {
p.RemoveValidator(testutils.TestAddress("totally random"))
})
})
}

func TestPoC_RemoveValidator(t *testing.T) {
t.Parallel()

var (
proposalAddress = testutils.TestAddress("caller")
initialSet = generateTestValidators(1)
)

initialSet[0].Address = proposalAddress

// Create the protocol with an initial set
p := NewPoC(WithInitialSet(initialSet))

// Attempt to remove the validator
testing.NotPanics(t, func() {
p.RemoveValidator(proposalAddress)
})

// Make sure the validator is removed
if p.IsValidator(proposalAddress) || len(p.validators) != 0 {
t.Fatal("address is validator")
}
}

func TestPoC_GetValidator(t *testing.T) {
t.Parallel()

t.Run("validator not in set", func(t *testing.T) {
t.Parallel()

// Create the protocol with no initial set
p := NewPoC()

// Attempt to get the voting power
testing.PanicsWithError(t, types.ErrValidatorMissing, func() {
p.GetValidator(testutils.TestAddress("caller"))
})
})

t.Run("validator fetched", func(t *testing.T) {
t.Parallel()

var (
address = testutils.TestAddress("caller")
pubKey = "public-key"
votingPower = uint64(10)

initialSet = generateTestValidators(1)
)

initialSet[0].Address = address
initialSet[0].PubKey = pubKey
initialSet[0].VotingPower = votingPower

// Create the protocol with an initial set
p := NewPoC(WithInitialSet(initialSet))

// Get the validator
val := p.GetValidator(address)

// Validate the address
if val.Address != address {
t.Fatal("invalid address")
}

// Validate the voting power
if val.VotingPower != votingPower {
t.Fatal("invalid voting power")
}

// Validate the public key
if val.PubKey != pubKey {
t.Fatal("invalid public key")
}
})
}
1 change: 1 addition & 0 deletions examples/gno.land/p/sys/vals/types/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/sys/vals/types
53 changes: 53 additions & 0 deletions examples/gno.land/p/sys/vals/types/types.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package types

import (
"std"
)

// ValsetProtocol defines the validator set protocol (PoA / PoS / PoC / ?)
type ValsetProtocol interface {
// AddValidator adds a new validator to the validator set.
// If the validator is already present, the method should error out
//
// TODO: This API is not ideal -- the address should be derived from
// the public key, and not be passed in as such, but currently Gno
// does not support crypto address derivation
AddValidator(address std.Address, pubKey string, power uint64) *Validator

// RemoveValidator removes the given validator from the set.
// If the validator is not present in the set, the method should error out
RemoveValidator(address std.Address) *Validator

// IsValidator returns a flag indicating if the given
// bech32 address is part of the validator set
IsValidator(address std.Address) bool

// GetValidator returns the validator using the given address
GetValidator(address std.Address) Validator

// GetValidators returns the currently active validator set
GetValidators() []*Validator
}

// Validator represents a single chain validator
type Validator struct {
Address std.Address // bech32 address
PubKey string // bech32 representation of the public key
VotingPower uint64
}

const (
ValidatorAddedEvent = "ValidatorAdded" // emitted when a validator was added to the set
ValidatorRemovedEvent = "ValidatorRemoved" // emitted when a validator was removed from the set
)

const (
// ErrCallerNotUserAccount is returned when the caller is not a user account
ErrCallerNotUserAccount = "caller not user account"

// ErrValidatorExists is returned when the validator is already in the set
ErrValidatorExists = "validator already exists"

// ErrValidatorMissing is returned when the validator is not in the set
ErrValidatorMissing = "validator doesn't exist"
)
2 changes: 1 addition & 1 deletion examples/gno.land/r/gnoland/home/home.gno
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func packageStaffPicks() ui.Element {
ui.BulletList{
ui.Link{URL: "r/sys/names"},
ui.Link{URL: "r/sys/rewards"},
ui.Link{URL: "r/sys/validators"},
ui.Link{URL: "r/sys/vals"},
},
}, {
ui.H4("[r/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo)"),
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/gnoland/home/home_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func main() {
//
// - [r/sys/names](r/sys/names)
// - [r/sys/rewards](r/sys/rewards)
// - [r/sys/validators](r/sys/validators)
// - [r/sys/vals](r/sys/vals)
//
// </div><!-- /column-->
// <div class="column">
Expand Down
Loading
Loading