mirror of https://github.com/poanetwork/gecko.git
119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package snowman
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/utils/logging"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
type polls struct {
|
|
log logging.Logger
|
|
numPolls prometheus.Gauge
|
|
alpha int
|
|
m map[uint32]poll
|
|
}
|
|
|
|
// Add to the current set of polls
|
|
// Returns true if the poll was registered correctly and the network sample
|
|
// should be made.
|
|
func (p *polls) Add(requestID uint32, numPolled int) bool {
|
|
poll, exists := p.m[requestID]
|
|
if !exists {
|
|
poll.alpha = p.alpha
|
|
poll.numPolled = numPolled
|
|
p.m[requestID] = poll
|
|
|
|
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
}
|
|
return !exists
|
|
}
|
|
|
|
// Vote registers the connections response to a query for [id]. If there was no
|
|
// query, or the response has already be registered, nothing is performed.
|
|
func (p *polls) Vote(requestID uint32, vdr ids.ShortID, vote ids.ID) (ids.Bag, bool) {
|
|
p.log.Verbo("[polls.Vote] Vote: requestID: %d. validatorID: %s. Vote: %s", requestID, vdr, vote)
|
|
poll, exists := p.m[requestID]
|
|
if !exists {
|
|
return ids.Bag{}, false
|
|
}
|
|
poll.Vote(vote)
|
|
if poll.Finished() {
|
|
delete(p.m, requestID)
|
|
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
return poll.votes, true
|
|
}
|
|
p.m[requestID] = poll
|
|
return ids.Bag{}, false
|
|
}
|
|
|
|
// CancelVote registers the connections failure to respond to a query for [id].
|
|
func (p *polls) CancelVote(requestID uint32, vdr ids.ShortID) (ids.Bag, bool) {
|
|
p.log.Verbo("CancelVote received. requestID: %d. validatorID: %s. Vote: %s", requestID, vdr)
|
|
poll, exists := p.m[requestID]
|
|
if !exists {
|
|
return ids.Bag{}, false
|
|
}
|
|
|
|
poll.CancelVote()
|
|
if poll.Finished() {
|
|
delete(p.m, requestID)
|
|
p.numPolls.Set(float64(len(p.m))) // Tracks performance statistics
|
|
return poll.votes, true
|
|
}
|
|
p.m[requestID] = poll
|
|
return ids.Bag{}, false
|
|
}
|
|
|
|
func (p *polls) String() string {
|
|
sb := strings.Builder{}
|
|
|
|
sb.WriteString(fmt.Sprintf("Current polls: (Size = %d)", len(p.m)))
|
|
for requestID, poll := range p.m {
|
|
sb.WriteString(fmt.Sprintf("\n %d: %s", requestID, poll))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// poll represents the current state of a network poll for a block
|
|
type poll struct {
|
|
alpha int
|
|
votes ids.Bag
|
|
numPolled int
|
|
}
|
|
|
|
// Vote registers a vote for this poll
|
|
func (p *poll) CancelVote() {
|
|
if p.numPolled > 0 {
|
|
p.numPolled--
|
|
}
|
|
}
|
|
|
|
// Vote registers a vote for this poll
|
|
func (p *poll) Vote(vote ids.ID) {
|
|
if p.numPolled > 0 {
|
|
p.numPolled--
|
|
p.votes.Add(vote)
|
|
}
|
|
}
|
|
|
|
// Finished returns true if the poll has completed, with no more required
|
|
// responses
|
|
func (p poll) Finished() bool {
|
|
received := p.votes.Len()
|
|
_, freq := p.votes.Mode()
|
|
return p.numPolled == 0 || // All k nodes responded
|
|
freq >= p.alpha || // An alpha majority has returned
|
|
received+p.numPolled < p.alpha // An alpha majority can never return
|
|
}
|
|
|
|
func (p poll) String() string {
|
|
return fmt.Sprintf("Waiting on %d chits", p.numPolled)
|
|
}
|