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

unittest for election.go #930

Merged
merged 2 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion common/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
gocache "github.com/patrickmn/go-cache"
)

// Cache is an anstract cache layer
// Cache is an abstract cache layer
type Cache interface {
Add(key []byte, value interface{}) error
Get(key []byte) (value interface{}, ok bool)
Expand Down
12 changes: 6 additions & 6 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (consensus *Consensus) startElection(height uint32, elc *election.Election)

electedBlockHash, ok := result.(common.Uint256)
if !ok {
return common.EmptyUint256, fmt.Errorf("Convert election result to block hash error")
return common.EmptyUint256, fmt.Errorf("convert election result to block hash error")
}

log.Infof("Elected block hash %s got %d/%d neighbor votes, weight: %d (%.2f%%)", electedBlockHash.ToHexString(), len(elc.GetNeighborIDsByVote(electedBlockHash)), elc.NeighborVoteCount(), absWeight, relWeight*100)
Expand Down Expand Up @@ -314,10 +314,10 @@ func (consensus *Consensus) saveAcceptedBlock(electedBlockHash common.Uint256) e

elc, loaded, err := consensus.loadOrCreateElection(block.Header.UnsignedHeader.Height)
if err != nil {
return fmt.Errorf("Error load election: %v", err)
return fmt.Errorf("error load election: %v", err)
}
if !loaded {
return fmt.Errorf("Election is created instead of loaded")
return fmt.Errorf("election is created instead of loaded")
}

neighborIDs := elc.GetNeighborIDsByVote(electedBlockHash)
Expand Down Expand Up @@ -371,12 +371,12 @@ func (consensus *Consensus) saveBlocksAcceptedDuringSync(startHeight uint32) err
value, ok := consensus.elections.Get(heightToKey(height))
consensus.electionsLock.RUnlock()
if !ok || value == nil {
return fmt.Errorf("Election at height %d not found in local cache", height)
return fmt.Errorf("election at height %d not found in local cache", height)
}

elc, ok := value.(*election.Election)
if !ok || elc == nil {
return fmt.Errorf("Convert election at height %d from cache error", height)
return fmt.Errorf("convert election at height %d from cache error", height)
}

result, _, _, err := elc.GetResult()
Expand All @@ -386,7 +386,7 @@ func (consensus *Consensus) saveBlocksAcceptedDuringSync(startHeight uint32) err

electedBlockHash, ok := result.(common.Uint256)
if !ok {
return fmt.Errorf("Convert election result to block hash error")
return fmt.Errorf("convert election result to block hash error")
}

block, err := consensus.getBlockProposal(electedBlockHash)
Expand Down
1 change: 1 addition & 0 deletions consensus/consensus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package consensus
14 changes: 7 additions & 7 deletions consensus/election/election.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ type Election struct {
// NewElection creates an election using the config provided.
func NewElection(config *Config) (*Election, error) {
if config.Duration == 0 {
return nil, errors.New("Election duration cannot be empty")
return nil, errors.New("election duration cannot be empty")
}

if config.MinVotingInterval > config.MaxVotingInterval {
return nil, fmt.Errorf("Min voting interval %v is greater than max voting interval %v", config.MinVotingInterval, config.MaxVotingInterval)
return nil, fmt.Errorf("min voting interval %v is greater than max voting interval %v", config.MinVotingInterval, config.MaxVotingInterval)
}

if config.GetWeight == nil {
Expand All @@ -68,7 +68,7 @@ func NewElection(config *Config) (*Election, error) {
// returns error.
func (election *Election) SetInitialVote(vote interface{}) error {
if election.HasStarted() {
return errors.New("Cannot set initial vote, election has started")
return errors.New("cannot set initial vote, election has started")
}

election.Lock()
Expand Down Expand Up @@ -100,7 +100,7 @@ func (election *Election) Start() bool {
return success
}

// Stop stops an election. Typically this should not be called directly.
// Stop stops an election. Typically, this should not be called directly.
func (election *Election) Stop() {
election.Lock()
election.state = stopped
Expand Down Expand Up @@ -129,7 +129,7 @@ func (election *Election) IsStopped() bool {
// ReceiveVote receives and saves a vote from a neighbor.
func (election *Election) ReceiveVote(neighborID, vote interface{}) error {
if election.IsStopped() {
return errors.New("Election has already stopped")
return errors.New("election has already stopped")
}

election.neighborVotes.Store(neighborID, vote)
Expand All @@ -142,7 +142,7 @@ func (election *Election) ReceiveVote(neighborID, vote interface{}) error {
return nil
}

// GetTxVoteChan returns the send vote channel, which should be used to send
// GetTxVoteChan returns the sending vote channel, which should be used to send
// votes to neighbors.
func (election *Election) GetTxVoteChan() <-chan interface{} {
return election.txVoteChan
Expand Down Expand Up @@ -194,7 +194,7 @@ func (election *Election) NeighborVoteCount() uint32 {
// PrefillNeighborVotes prefills vote for neighborIDs that don't have a vote yet
func (election *Election) PrefillNeighborVotes(neighborIDs []interface{}, vote interface{}) error {
if election.IsStopped() {
return errors.New("Election has already stopped")
return errors.New("election has already stopped")
}

for _, neighborID := range neighborIDs {
Expand Down
155 changes: 155 additions & 0 deletions consensus/election/election_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package election

import (
"github.com/nknorg/nkn/v2/common"
"math"
"os"
"reflect"
"strconv"
"sync"
"testing"
"time"
)

var c = &Config{
Duration: 5 * time.Second,
MinVotingInterval: 200 * time.Millisecond,
MaxVotingInterval: 2 * time.Second,
ChangeVoteMinRelativeWeight: 0.5,
ConsensusMinRelativeWeight: 2.0 / 3.0,
}

func TestMain(m *testing.M) {
os.Exit(m.Run())
}

func check(t *testing.T, f string, got, want interface{}) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
}
}

func TestSingleElection(t *testing.T) {
elc, err := NewElection(c)
if err != nil {
t.Fatal(err)
}

err = elc.SetInitialVote(common.MaxUint256)
if err != nil {
t.Fatal(err)
}

v1 := common.EmptyUint256
v1[0] = 255

err = elc.PrefillNeighborVotes([]interface{}{"n1", "n2", "n3"}, v1)
if err != nil {
t.Fatal(err)
}

success := elc.Start()
if !success {
t.Fatal("failed to start election")
}

txVoteChan := elc.GetTxVoteChan()

for vote := range txVoteChan {
v, ok := vote.(common.Uint256)
if !ok {
t.Fatal("unexpected vote type")
}
check(t, "vote", v, v1)
}

result, absWeight, relWeight, err := elc.GetResult()
if err != nil {
t.Fatal(err)
}

check(t, "result", result, v1)
check(t, "absWeight", absWeight, uint32(4))
check(t, "relWeight", almostEqual(float64(relWeight), 1), true)
}

func TestMultiElection(t *testing.T) {
v1 := common.EmptyUint256
v1[0] = 1

elcList := make([]*Election, 20)

var err error
for i := 0; i < len(elcList); i++ {
elcList[i], err = NewElection(c)
if err != nil {
t.Fatal(err)
}
var v common.Uint256
if i < 14 {
v = v1
} else {
v = common.MaxUint256
}
err = elcList[i].SetInitialVote(v)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < len(elcList); i++ {
for j := 0; j < len(elcList); j++ {
if i != j {
err = elcList[i].PrefillNeighborVotes([]interface{}{strconv.Itoa(j)}, elcList[j].selfVote)
if err != nil {
t.Fatal(err)
}
}
}
}

for i := 0; i < len(elcList); i++ {
go func(i int) {
success := elcList[i].Start()
if !success {
t.Error("failed to start election:", i)
return
}
}(i)
}

var wg sync.WaitGroup
for i := 0; i < len(elcList); i++ {
yilunzhang marked this conversation as resolved.
Show resolved Hide resolved
wg.Add(1)
go func(i int) {
defer wg.Done()
txVoteChan := elcList[i].GetTxVoteChan()
for vote := range txVoteChan {
for j, n := range elcList {
if i != j {
err = n.ReceiveVote(strconv.Itoa(i), vote)
if err != nil {
t.Error(err)
yilunzhang marked this conversation as resolved.
Show resolved Hide resolved
return
}
}
}
}
}(i)
}

wg.Wait()

for _, n := range elcList {
result, absWeight, relWeight, err := n.GetResult()
if err != nil {
t.Fatal(err)
}
check(t, "result", result, v1)
check(t, "absWeight", absWeight, uint32(20))
check(t, "relWeight", almostEqual(float64(relWeight), 1), true)
}
}

func almostEqual(f1 float64, f2 float64) bool {
return math.Abs(f1-f2) < 1e-9
}
2 changes: 1 addition & 1 deletion node/remotenode.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (remoteNode *RemoteNode) SendBytesSync(buf []byte) ([]byte, error) {
func (remoteNode *RemoteNode) SendBytesSyncWithTimeout(buf []byte, replyTimeout time.Duration) ([]byte, error) {
reply, _, err := remoteNode.localNode.GetNnet().SendBytesDirectSyncWithTimeout(buf, remoteNode.NnetNode, replyTimeout)
if err != nil {
log.Debugf("Error sending sync messge to node: %v", err.Error())
log.Debugf("Error sending sync message to node: %v", err.Error())
}
return reply, err
}
Expand Down