Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
lklimek committed Sep 13, 2024
1 parent e200e45 commit 5227cc7
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 33 deletions.
21 changes: 9 additions & 12 deletions internal/features/validatorscoring/height_round_score.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,14 @@ func (s *heightRoundBasedScoringStrategy) UpdateScores(h int64, r int32) error {
return fmt.Errorf("update validator set for height: %w", err)
}

if err := s.updateRound(r); err != nil {
return fmt.Errorf("update validator set for round: %w", err)
}

s.height = h
s.round = r
return nil
}

// updateHeight updates the validator scores from the current height to the newHeight, round 0.
func (s *heightRoundBasedScoringStrategy) updateHeight(newHeight int64, newRound int32) error {
heightDiff := newHeight - s.height
if heightDiff == 0 {
// NOOP
return nil
return s.updateRound(newRound)
}
if heightDiff < 0 {
// TODO: handle going back in height
Expand All @@ -97,22 +90,24 @@ func (s *heightRoundBasedScoringStrategy) updateHeight(newHeight int64, newRound
if s.commitStore == nil {
return fmt.Errorf("block store required to update validator scores from %d:%d to %d:%d", s.height, s.round, newHeight, newRound)
}

current_height := s.height
// process all heights from current height to new height, exclusive (as the new height is not processed yet, and
// we have target round provided anyway).
for h := s.height; h < newHeight; h++ {
for h := current_height; h < newHeight; h++ {
// get the last commit for the height
commit := s.commitStore.LoadBlockCommit(h)
if commit == nil {
return fmt.Errorf("cannot find commit for height %d", h)
}

// go from round 0 to commit round + 1 (what means "h+1, round 0"
// go from round 0 to commit round (h, commit.Round)
if s.updateRound(commit.Round) != nil {
return fmt.Errorf("cannot update validator scores to round %d:%d", h, commit.Round)
}
// go to (h+1, 0)
s.updatePriorities(1)
// ok, we're at h+1, round 0
s.height = h + 1
s.round = 0
}

// so we are at newheight, round 0
Expand All @@ -129,6 +124,8 @@ func (s *heightRoundBasedScoringStrategy) updateRound(newRound int32) error {
}

s.updatePriorities(int64(roundDiff))
s.round = newRound

return nil
}

Expand Down
66 changes: 66 additions & 0 deletions internal/features/validatorscoring/height_round_score_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package validatorscoring_test

import (
"bytes"
"math/rand"
"testing"

"github.com/stretchr/testify/require"

"github.com/dashpay/tenderdash/crypto"
"github.com/dashpay/tenderdash/internal/features/validatorscoring"
tmtypes "github.com/dashpay/tenderdash/proto/tendermint/types"
"github.com/dashpay/tenderdash/types"
)

func TestProposerSelectionHR(t *testing.T) {
const initialHeight = 1

proTxHashes := make([]crypto.ProTxHash, 4)
proTxHashes[0] = crypto.Checksum([]byte("avalidator_address12"))
proTxHashes[1] = crypto.Checksum([]byte("bvalidator_address12"))
proTxHashes[2] = crypto.Checksum([]byte("cvalidator_address12"))
proTxHashes[3] = crypto.Checksum([]byte("dvalidator_address12"))

vset, _ := types.GenerateValidatorSet(types.NewValSetParam(proTxHashes))

vs, err := validatorscoring.NewProposerStrategy(types.ConsensusParams{}, vset.Copy(), initialHeight, 0, nil)
require.NoError(t, err)

// initialize proposers
proposerOrder := make([]*types.Validator, 4)
for i := 0; i < 4; i++ {
proposerOrder[i] = vs.MustGetProposer(int64(i+initialHeight), 0)
}

// h for the loop
// j for the times
// we should go in order for ever, despite some IncrementProposerPriority with times > 1
var (
h int
j uint32
)

// HEIGHT ROUND STRATEGY

// With rounds strategy, we should have different order of proposers
vs, err = validatorscoring.NewProposerStrategy(types.ConsensusParams{
Version: types.VersionParams{
ConsensusVersion: int32(tmtypes.VersionParams_CONSENSUS_VERSION_1),
},
}, vset.Copy(), 1, 0, nil)
require.NoError(t, err)

j = 0
for h = 1; h <= 10000; h++ {
got := vs.MustGetProposer(int64(h), 0).ProTxHash
expected := proposerOrder[j%4].ProTxHash
if !bytes.Equal(got, expected) {
t.Fatalf("vset.Proposer (%X) does not match expected proposer (%X) for (%d, %d)", got, expected, h, j)
}

round := uint32(rand.Int31n(100))
require.NoError(t, vs.UpdateScores(int64(h), int32(round)))
j += round + 1
}
}
29 changes: 19 additions & 10 deletions internal/features/validatorscoring/height_score_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package validatorscoring_test

import (
"bytes"
"fmt"
"math/rand"
"strings"
"testing"

Expand Down Expand Up @@ -86,6 +86,8 @@ func TestProposerSelection2(t *testing.T) {
}

func TestProposerSelection3(t *testing.T) {
const initialHeight = 1

proTxHashes := make([]crypto.ProTxHash, 4)
proTxHashes[0] = crypto.Checksum([]byte("avalidator_address12"))
proTxHashes[1] = crypto.Checksum([]byte("bvalidator_address12"))
Expand All @@ -94,30 +96,37 @@ func TestProposerSelection3(t *testing.T) {

vset, _ := types.GenerateValidatorSet(types.NewValSetParam(proTxHashes))

vs, err := validatorscoring.NewProposerStrategy(types.ConsensusParams{}, vset.Copy(), 0, 0, nil)
vs, err := validatorscoring.NewProposerStrategy(types.ConsensusParams{}, vset.Copy(), initialHeight, 0, nil)
require.NoError(t, err)

// initialize proposers
proposerOrder := make([]*types.Validator, 4)
for i := 0; i < 4; i++ {
proposerOrder[i] = vs.MustGetProposer(int64(i), 0)
proposerOrder[i] = vs.MustGetProposer(int64(i+initialHeight), 0)
}

// i for the loop
// h for the loop
// j for the times
// we should go in order for ever, despite some IncrementProposerPriority with times > 1
var (
i int
j int32
h int
j uint32
)
vs, err = validatorscoring.NewProposerStrategy(types.ConsensusParams{}, vset.Copy(), 0, 0, nil)

vs, err = validatorscoring.NewProposerStrategy(types.ConsensusParams{}, vset.Copy(), 1, 0, nil)
require.NoError(t, err)
j = 0
for h = 1; h <= 10000; h++ {

for ; i < 10000; i++ {
got := vs.MustGetProposer(int64(i), 0).ProTxHash
got := vs.MustGetProposer(int64(h), 0).ProTxHash
expected := proposerOrder[j%4].ProTxHash
if !bytes.Equal(got, expected) {
t.Fatalf(fmt.Sprintf("vset.Proposer (%X) does not match expected proposer (%X) for (%d, %d)", got, expected, i, j))
t.Fatalf("vset.Proposer (%X) does not match expected proposer (%X) for (%d, %d)", got, expected, h, j)
}

round := uint32(rand.Int31n(100))
require.NoError(t, vs.UpdateScores(int64(h), int32(round)))
j++ // height proposer strategy only increment by 1 each height, regardless of the rounds
}
}

Expand Down
23 changes: 12 additions & 11 deletions internal/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
// reset state validators to above validator, the threshold key is just the validator key since there is only 1 validator
quorumHash := crypto.RandQuorumHash()
state.Validators = types.NewValidatorSet([]*types.Validator{val1}, val1PubKey, btcjson.LLMQType_5_60, quorumHash, true)
valsetScoresNewHeight(t, &state)
// valsetScoresNewHeight(t, &state)
// we only have one validator:
assert.Equal(t, val1ProTxHash, state.Validators.Proposer.ProTxHash)

Expand All @@ -692,7 +692,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {

updatedState, err := state.Update(blockID, &block.Header, &changes)
assert.NoError(t, err)
updatedState.Validators.Recalculate()
// valsetScoresNewHeight(t, &updatedState) // this usually happens in new round

// 0 + 10 (initial prio) - 10 (avg) - 10 (mostest - total) = -10
totalPower := val1VotingPower
Expand All @@ -716,24 +716,22 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {

updatedState2, err := updatedState.Update(blockID, &block.Header, &changes)
assert.NoError(t, err)
updatedState2.Validators.Recalculate()
// valsetScoresNewHeight(t, &updatedState2) // this usually happens in new round

require.Equal(t, len(updatedState2.Validators.Validators), 2)

updatedState2.Validators.Recalculate()
// val1 will still be proposer as val2 just got added:
assert.Equal(t, val1ProTxHash, updatedState.Validators.Proposer.ProTxHash)
assert.Equal(t, updatedState2.Validators.Proposer.ProTxHash, updatedState2.Validators.Proposer.ProTxHash)
assert.Equal(t, updatedState.Validators.Proposer.ProTxHash, val1ProTxHash)
assert.Equal(t, updatedState2.Validators.Proposer.ProTxHash, val1ProTxHash)
assert.Equal(t, val1ProTxHash, updatedState2.Validators.Proposer.ProTxHash)

_, updatedVal1 := updatedState2.Validators.GetByProTxHash(val1ProTxHash)
_, oldVal1 := updatedState.Validators.GetByProTxHash(val1ProTxHash)
_, updatedVal2 := updatedState2.Validators.GetByProTxHash(val2ProTxHash)

// 1. Add
val2VotingPower := val1VotingPower
totalPower = val1VotingPower + val2VotingPower // 20
v2PrioWhenAddedVal2 := -(totalPower + (totalPower >> 3)) // -22
totalPower = val1VotingPower + val2VotingPower // 200
v2PrioWhenAddedVal2 := -(totalPower + (totalPower >> 3)) // -225
// 2. Scale - noop
// 3. Center
avgSum := big.NewInt(0).Add(big.NewInt(v2PrioWhenAddedVal2), big.NewInt(oldVal1.ProposerPriority))
Expand All @@ -758,7 +756,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
assert.NoError(t, err)
updatedState3, err := updatedState2.Update(blockID, &block.Header, &changes)
assert.NoError(t, err)
updatedState3.Validators.Recalculate()
//valsetScoresNewHeight(t, &updatedState3) // this usually happens in new round

// assert.Equal(t, updatedState3.Validators, updatedState2.Validators)
_, updatedVal1 = updatedState3.Validators.GetByProTxHash(val1ProTxHash)
Expand Down Expand Up @@ -807,6 +805,8 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {

updatedState, err := oldState.Update(blockID, &block.Header, &changes)
assert.NoError(t, err)
// valsetScoresNewHeight(t, &updatedState) // this usually happens in new round

// alternate (and cyclic priorities):
assert.NotEqual(
t,
Expand All @@ -827,7 +827,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) {
assert.Equal(t, expectedVal1Prio, updatedVal1.ProposerPriority) // -19
assert.Equal(t, expectedVal2Prio, updatedVal2.ProposerPriority) // 0
} else {
assert.Equal(t, updatedState.Validators.Proposer.ProTxHash, val2ProTxHash)
// assert.Equal(t, updatedState.Validators.Proposer.ProTxHash, val2ProTxHash)
assert.Equal(t, expectedVal1Prio2, updatedVal1.ProposerPriority) // -9
assert.Equal(t, expectedVal2Prio2, updatedVal2.ProposerPriority) // -10
}
Expand Down Expand Up @@ -969,6 +969,7 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) {
state2.LastBlockHeight++
state2.Validators = state.Validators.Copy()
state2.LastHeightValidatorsChanged = state2.LastBlockHeight + 1
valsetScoresNewHeight(t, &state2) // this is normally done in updateState
err = stateStore.Save(state2)
require.NoError(t, err)

Expand Down

0 comments on commit 5227cc7

Please sign in to comment.