gecko/snow/engine/avalanche/voter.go

115 lines
2.9 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avalanche
import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
)
type voter struct {
t *Transitive
vdr ids.ShortID
requestID uint32
response ids.Set
deps ids.Set
}
func (v *voter) Dependencies() ids.Set { return v.deps }
func (v *voter) Fulfill(id ids.ID) {
v.deps.Remove(id)
v.Update()
}
func (v *voter) Abandon(id ids.ID) { v.Fulfill(id) }
func (v *voter) Update() {
if v.deps.Len() != 0 || v.t.errs.Errored() {
return
}
results, finished := v.t.polls.Vote(v.requestID, v.vdr, v.response.List())
if !finished {
return
}
results = v.bubbleVotes(results)
v.t.Config.Context.Log.Debug("Finishing poll with:\n%s", &results)
if err := v.t.Consensus.RecordPoll(results); err != nil {
v.t.errs.Add(err)
return
}
txs := []snowstorm.Tx(nil)
for _, orphanID := range v.t.Consensus.Orphans().List() {
if tx, err := v.t.Config.VM.GetTx(orphanID); err == nil {
txs = append(txs, tx)
} else {
v.t.Config.Context.Log.Warn("Failed to fetch %s during attempted re-issuance", orphanID)
}
}
if len(txs) > 0 {
v.t.Config.Context.Log.Debug("Re-issuing %d transactions", len(txs))
}
if err := v.t.batch(txs, true /*=force*/, false /*empty*/); err != nil {
v.t.errs.Add(err)
return
}
if v.t.Consensus.Quiesce() {
v.t.Config.Context.Log.Debug("Avalanche engine can quiesce")
return
}
v.t.Config.Context.Log.Debug("Avalanche engine can't quiesce")
v.t.errs.Add(v.t.repoll())
}
func (v *voter) bubbleVotes(votes ids.UniqueBag) ids.UniqueBag {
bubbledVotes := ids.UniqueBag{}
vertexHeap := newMaxVertexHeap()
for _, vote := range votes.List() {
vtx, err := v.t.Config.State.GetVertex(vote)
if err != nil {
continue
}
vertexHeap.Push(vtx)
}
for vertexHeap.Len() > 0 {
vtx := vertexHeap.Pop()
vtxID := vtx.ID()
set := votes.GetSet(vtxID)
status := vtx.Status()
if !status.Fetched() {
v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is unknown", set.Len(), vtxID)
bubbledVotes.RemoveSet(vtx.ID())
continue
}
if status.Decided() {
v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is decided", set.Len(), vtxID)
bubbledVotes.RemoveSet(vtx.ID())
continue
}
if v.t.Consensus.VertexIssued(vtx) {
v.t.Config.Context.Log.Verbo("Applying %d vote(s) for %s", set.Len(), vtx.ID())
bubbledVotes.UnionSet(vtx.ID(), set)
} else {
v.t.Config.Context.Log.Verbo("Bubbling %d vote(s) for %s because the vertex isn't issued", set.Len(), vtx.ID())
bubbledVotes.RemoveSet(vtx.ID()) // Remove votes for this vertex because it hasn't been issued
for _, parentVtx := range vtx.Parents() {
bubbledVotes.UnionSet(parentVtx.ID(), set)
vertexHeap.Push(parentVtx)
}
}
}
return bubbledVotes
}