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: add /r/sys/validators #2130

Merged
merged 62 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
62 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
b5e10f1
Use AVL to manage valsets
zivkovicmilos Jul 1, 2024
122e178
Simplify ValsetProtocol API
zivkovicmilos Jul 1, 2024
80fe0b4
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jul 1, 2024
5371852
Apply renames
zivkovicmilos Jul 1, 2024
e45814b
Tidy mods
zivkovicmilos Jul 1, 2024
96e1364
Rename vals -> validators
zivkovicmilos Jul 1, 2024
5e9c3e0
Update examples/gno.land/r/gnoland/home/home.gno
moul Jul 1, 2024
0f2fb39
Update examples/gno.land/r/gnoland/home/home_filetest.gno
moul Jul 1, 2024
da51c23
Update examples/gno.land/r/sys/validators/poc.gno
zivkovicmilos Jul 2, 2024
b25171d
Move getChanges to gnosdk.gno
zivkovicmilos Jul 2, 2024
fa341a6
Simplify valset to global package vars
zivkovicmilos Jul 2, 2024
a0fc2c1
Standardize return API for ValsetProtocol
zivkovicmilos Jul 2, 2024
55cdfb2
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jul 2, 2024
cc6bc62
Merge master
zivkovicmilos Jul 2, 2024
48d6471
Merge branch 'master' into dev/zivkovicmilos/sys-vals
zivkovicmilos Jul 2, 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/nt/poa/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module gno.land/p/nt/poa

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/validators v0.0.0-latest
)
14 changes: 14 additions & 0 deletions examples/gno.land/p/nt/poa/option.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package poa

import "gno.land/p/sys/validators"

type Option func(*PoA)

// WithInitialSet sets the initial PoA validator set
func WithInitialSet(validators []validators.Validator) Option {
return func(p *PoA) {
for _, validator := range validators {
p.validators.Set(validator.Address.String(), validator)
}
}
}
106 changes: 106 additions & 0 deletions examples/gno.land/p/nt/poa/poa.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package poa

import (
"errors"
"std"

"gno.land/p/demo/avl"
"gno.land/p/sys/validators"
)

var ErrInvalidVotingPower = errors.New("invalid voting power")

// PoA specifies the Proof of Authority validator set, with simple add / remove constraints.
//
// To add:
// - proposed validator must not be part of the set already
// - proposed validator voting power must be > 0
//
// To remove:
// - proposed validator must be part of the set already
type PoA struct {
validators *avl.Tree // std.Address -> validators.Validator
}

// NewPoA creates a new empty Proof of Authority validator set
func NewPoA(opts ...Option) *PoA {
// Create the empty set
p := &PoA{
validators: avl.NewTree(),
}

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

return p
}

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

// Make sure the voting power > 0
if power == 0 {
return nil, ErrInvalidVotingPower
}

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

// Add the validator to the set
p.validators.Set(address.String(), v)

return &v, nil
moul marked this conversation as resolved.
Show resolved Hide resolved
}

func (p *PoA) RemoveValidator(address std.Address) (*validators.Validator, error) {
// Validate that the operation is a valid call
// Fetch the validator
validator, err := p.GetValidator(address)
if err != nil {
return nil, err
}

// Remove the validator from the set
p.validators.Remove(address.String())

return validator, nil
}

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

return exists
}

func (p *PoA) GetValidator(address std.Address) (*validators.Validator, error) {
validatorRaw, exists := p.validators.Get(address.String())
if !exists {
return nil, validators.ErrValidatorMissing
}

validator := validatorRaw.(validators.Validator)

return &validator, nil
}

func (p *PoA) GetValidators() []validators.Validator {
vals := make([]validators.Validator, 0, p.validators.Size())

p.validators.Iterate("", "", func(_ string, value interface{}) bool {
validator := value.(validators.Validator)
vals = append(vals, validator)

return false
})

return vals
}
239 changes: 239 additions & 0 deletions examples/gno.land/p/nt/poa/poa_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package poa

import (
"testing"

"gno.land/p/demo/testutils"
"gno.land/p/sys/validators"

"gno.land/p/demo/ufmt"
)

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

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

vals = append(vals, val)
}

return vals
}

func TestPoA_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 := NewPoA(WithInitialSet(initialSet))

// Attempt to add the validator
_, err := p.AddValidator(proposalAddress, proposalKey, 1)
testing.ErrorIs(t, err, validators.ErrValidatorExists)
})

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 := NewPoA()

// Attempt to add the validator
_, err := p.AddValidator(proposalAddress, proposalKey, 0)
testing.ErrorIs(t, err, ErrInvalidVotingPower)
})
}

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

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

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

// Attempt to add the validator
_, err := p.AddValidator(proposalAddress, proposalKey, 1)
testing.NoError(t, err)

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

func TestPoA_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 := NewPoA(WithInitialSet(initialSet))

// Attempt to remove the validator
_, err := p.RemoveValidator(testutils.TestAddress("totally random"))
testing.ErrorIs(t, err, validators.ErrValidatorMissing)
})
}

func TestPoA_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 := NewPoA(WithInitialSet(initialSet))

// Attempt to remove the validator
_, err := p.RemoveValidator(proposalAddress)
testing.NoError(t, err)

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

func TestPoA_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 := NewPoA()

// Attempt to get the voting power
_, err := p.GetValidator(testutils.TestAddress("caller"))
testing.ErrorIs(t, err, validators.ErrValidatorMissing)
})

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 := NewPoA(WithInitialSet(initialSet))

// Get the validator
val, err := p.GetValidator(address)
testing.NoError(t, err)

// 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")
}
})
}

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

t.Run("empty set", func(t *testing.T) {
t.Parallel()

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

// Attempt to get the voting power
vals := p.GetValidators()

if len(vals) != 0 {
t.Fatal("validator set is not empty")
}
})

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

initialSet := generateTestValidators(10)

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

// Get the validator set
vals := p.GetValidators()

if len(vals) != len(initialSet) {
t.Fatal("returned validator set mismatch")
}

for _, val := range vals {
for _, initialVal := range initialSet {
if val.Address != initialVal.Address {
continue
}

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

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