forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add valset injection through
r/sys/validators
(gnolang#2229)
## Description This PR introduces an `EndBlocker` system for applying validator set changes protocol level, based on on-chain events (from the `/r/sys/vals` realm). I've utilized an already existing system: - validator set changes still stay managed protocol-level in `ConsensusState` -> refactoring this is not a small feat, and saying this is an understatement - event switch utilized by the node that dumps new block / new transaction events The way this flow essentially works is the following: 1. An on-chain event happens that indicates a change in the validator set (added / removed) 2. These events (ABCI events) are parsed as soon as they end up in a transaction result (are pushed to the event system of the SDK). The top-level ABCI event type needs to be`EventTx` (indicating it's a new TX result). The underlying tx GnoVM events (`GnoEvent`) need to be from the `/r/sys/validators` Realm, and be a validator addition / removal (type defined in the Realm) 4. Events are parsed down into `abci.ValidatorUpdates`, which are returned as a result of `EndBlocker` 5. This `EndBlocker` result is later read by the `ConsensusState`, and the validator set changes are applied for the upcoming block in a series of existing callbacks. This also keeps proposer priority logic in check. Blocked by gnolang#2130 Closes gnolang#1823 ```mermaid --- title: on-chain validator set injection flow --- flowchart TD subgraph app.go nesvw(["Node event switch"]) -. pass all block events .-> collector(["event collector"]) collector -. subscribes to .-> nesvw collector -. filter new events .-> collector EB["func EndBlocker(...)"] == 1: fetch relevant events ==> collector collector -. 2: return events, if any .-> EB end subgraph gno.land/r/sys/validators.gno GC["func GetChanges(from int64) []validators.Validator"] addVal["func addValidator(...)"] removeVal["func removeValidator(...)"] PE["func NewPropExecutor(changesFn) proposal.Executor"] PE -. calls internally .-> addVal PE -. calls internally .-> removeVal addVal -. std emits ValidatorAdded .-> nesvw removeVal -. std emits ValidatorRemoved .-> nesvw end subgraph gno.land/r/gov/dao.gno EP["func ExecuteProposal(...)"] EP == executes on-chain ==>PE end subgraph user_proposal.gno main("func main() {...}") PR["govdao.Propose(...)"] main -. contains .-> CB main -. contains .-> PR CB("changesFn func() []validators.Validator {...}") CB== creates ==>PE CB("changesFn func() []validators.Validator {...}") PE == passed into ==> PR end A[/fa:fa-user User\] == gnokey maketx run ==> main GDV[/fa:fa-people-group GOVDAO members\] == manually call ==> EP EB == 3: execute VM call to get changes since last block ==> GC GC -. 4: return changes .-> EB EB -. 5: return response with valset changes .-> EBR([abci.ResponseEndBlock]) -- applied in --> AB subgraph Cosmos SDK AB["func ApplyBlock(...) {...}"] end ``` Related: gnolang#1945 <details><summary>Contributors' checklist...</summary> - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details> --------- Co-authored-by: Manfred Touron <[email protected]>
- Loading branch information
1 parent
b1d778c
commit b5560e2
Showing
25 changed files
with
881 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,24 @@ | ||
package validators | ||
|
||
import "gno.land/p/sys/validators" | ||
import ( | ||
"gno.land/p/sys/validators" | ||
) | ||
|
||
// getChanges returns the validator changes stored on the realm. | ||
// This function is unexported and intended to be called by gno.land through the GnoSDK | ||
func getChanges() []validators.Validator { | ||
// Construct the changes | ||
valsetChanges := make([]validators.Validator, len(changes)) | ||
copy(valsetChanges, changes) | ||
// GetChanges returns the validator changes stored on the realm, since the given block number. | ||
// This function is intended to be called by gno.land through the GnoSDK | ||
func GetChanges(from int64) []validators.Validator { | ||
valsetChanges := make([]validators.Validator, 0) | ||
|
||
// Reset the changes set | ||
changes = changes[:0] | ||
// Gather the changes from the specified block | ||
changes.Iterate(getBlockID(from), "", func(_ string, value interface{}) bool { | ||
chs := value.([]change) | ||
|
||
for _, ch := range chs { | ||
valsetChanges = append(valsetChanges, ch.validator) | ||
} | ||
|
||
return false | ||
}) | ||
|
||
return valsetChanges | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
package validators | ||
|
||
import ( | ||
"gno.land/p/demo/avl" | ||
"gno.land/p/nt/poa" | ||
"gno.land/p/sys/validators" | ||
) | ||
|
||
func init() { | ||
// The default valset protocol is PoA | ||
vp = poa.NewPoA() | ||
|
||
// No changes to apply initially | ||
changes = make([]validators.Validator, 0) | ||
changes = avl.NewTree() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package validators | ||
|
||
import ( | ||
"testing" | ||
|
||
"std" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/testutils" | ||
"gno.land/p/demo/uassert" | ||
"gno.land/p/demo/ufmt" | ||
"gno.land/p/sys/validators" | ||
) | ||
|
||
// 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: 10, | ||
} | ||
|
||
vals = append(vals, val) | ||
} | ||
|
||
return vals | ||
} | ||
|
||
func TestValidators_AddRemove(t *testing.T) { | ||
// Clear any changes | ||
changes = avl.NewTree() | ||
|
||
var ( | ||
vals = generateTestValidators(100) | ||
initialHeight = int64(123) | ||
) | ||
|
||
// Add in the validators | ||
for _, val := range vals { | ||
addValidator(val) | ||
|
||
// Make sure the validator is added | ||
uassert.True(t, vp.IsValidator(val.Address)) | ||
|
||
std.TestSkipHeights(1) | ||
} | ||
|
||
for i := initialHeight; i < initialHeight+int64(len(vals)); i++ { | ||
// Make sure the changes are saved | ||
chs := GetChanges(i) | ||
|
||
// We use the funky index calculation to make sure | ||
// changes are properly handled for each block span | ||
uassert.Equal(t, initialHeight+int64(len(vals))-i, int64(len(chs))) | ||
|
||
for index, val := range vals[i-initialHeight:] { | ||
// Make sure the changes are equal to the additions | ||
ch := chs[index] | ||
|
||
uassert.Equal(t, val.Address, ch.Address) | ||
uassert.Equal(t, val.PubKey, ch.PubKey) | ||
uassert.Equal(t, val.VotingPower, ch.VotingPower) | ||
} | ||
} | ||
|
||
// Save the beginning height for the removal | ||
initialRemoveHeight := std.GetHeight() | ||
|
||
// Clear any changes | ||
changes = avl.NewTree() | ||
|
||
// Remove the validators | ||
for _, val := range vals { | ||
removeValidator(val.Address) | ||
|
||
// Make sure the validator is removed | ||
uassert.False(t, vp.IsValidator(val.Address)) | ||
|
||
std.TestSkipHeights(1) | ||
} | ||
|
||
for i := initialRemoveHeight; i < initialRemoveHeight+int64(len(vals)); i++ { | ||
// Make sure the changes are saved | ||
chs := GetChanges(i) | ||
|
||
// We use the funky index calculation to make sure | ||
// changes are properly handled for each block span | ||
uassert.Equal(t, initialRemoveHeight+int64(len(vals))-i, int64(len(chs))) | ||
|
||
for index, val := range vals[i-initialRemoveHeight:] { | ||
// Make sure the changes are equal to the additions | ||
ch := chs[index] | ||
|
||
uassert.Equal(t, val.Address, ch.Address) | ||
uassert.Equal(t, val.PubKey, ch.PubKey) | ||
uassert.Equal(t, uint64(0), ch.VotingPower) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.