forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 4
/
voter.go
140 lines (118 loc) · 4 KB
/
voter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package snowman
import (
"github.com/MetalBlockchain/metalgo/ids"
)
// Voter records chits received from [vdr] once its dependencies are met.
type voter struct {
t *Transitive
vdr ids.ShortID
requestID uint32
response ids.ID
deps ids.Set
}
func (v *voter) Dependencies() ids.Set { return v.deps }
// Mark that a dependency has been met.
func (v *voter) Fulfill(id ids.ID) {
v.deps.Remove(id)
v.Update()
}
// Abandon this attempt to record chits.
func (v *voter) Abandon(id ids.ID) { v.Fulfill(id) }
func (v *voter) Update() {
if v.deps.Len() != 0 || v.t.errs.Errored() {
return
}
var results []ids.Bag
if v.response == ids.Empty {
results = v.t.polls.Drop(v.requestID, v.vdr)
} else {
results = v.t.polls.Vote(v.requestID, v.vdr, v.response)
}
if len(results) == 0 {
return
}
// To prevent any potential deadlocks with un-disclosed dependencies, votes
// must be bubbled to the nearest valid block
for i, result := range results {
results[i] = v.bubbleVotes(result)
}
for _, result := range results {
result := result
v.t.Ctx.Log.Debug("Finishing poll with:\n%s", &result)
if err := v.t.Consensus.RecordPoll(result); err != nil {
v.t.errs.Add(err)
}
}
if v.t.errs.Errored() {
return
}
if err := v.t.VM.SetPreference(v.t.Consensus.Preference()); err != nil {
v.t.errs.Add(err)
return
}
if v.t.Consensus.Finalized() {
v.t.Ctx.Log.Debug("Snowman engine can quiesce")
return
}
v.t.Ctx.Log.Debug("Snowman engine can't quiesce")
v.t.repoll()
}
// bubbleVotes bubbles the [votes] a set of the number of votes for specific
// blkIDs that received votes in consensus, to their most recent ancestor that
// has been issued to consensus.
//
// Note: bubbleVotes does not bubbleVotes to all of the ancestors in consensus,
// just the most recent one. bubbling to the rest of the ancestors, which may
// also be in consensus is handled in RecordPoll.
func (v *voter) bubbleVotes(votes ids.Bag) ids.Bag {
bubbledVotes := ids.Bag{}
votesLoop:
for _, vote := range votes.List() {
count := votes.Count(vote)
// use rootID in case of this is a non-verified block ID
rootID := v.t.nonVerifieds.GetRoot(vote)
v.t.Ctx.Log.Verbo("Bubbling %d vote(s) for %s to %s through unverified blocks", count, vote, rootID)
blk, err := v.t.GetBlock(rootID)
// If we cannot retrieve the block, drop [vote]
if err != nil {
v.t.Ctx.Log.Debug("Dropping %d vote(s) for %s because %s couldn't be fetched", count, vote, rootID)
continue
}
status := blk.Status()
blkID := blk.ID()
// If we have not fetched [blkID] break from the loop. We will drop the
// vote below and move on to the next vote.
//
// If [blk] has already been decided, break from the loop, we will drop
// the vote below since there is no need to count the votes for a [blk]
// we've already finalized.
//
// If [blk] is currently in consensus, break from the loop, we have
// reached the first ancestor of the original [vote] that has been
// issued consensus. In this case, the votes will be bubbled further
// from [blk] to any of its ancestors that are also in consensus.
for status.Fetched() && !(v.t.Consensus.Decided(blk) || v.t.Consensus.Processing(blkID)) {
parentID := blk.Parent()
v.t.Ctx.Log.Verbo("Pushing %d vote(s) from %s (%s) to %s", count, blkID, status, parentID)
blkID = parentID
blk, err = v.t.GetBlock(blkID)
// If we cannot retrieve the block, drop [vote]
if err != nil {
v.t.Ctx.Log.Debug("Dropping %d vote(s) for %s because %s couldn't be fetched",
count, vote, blkID)
continue votesLoop
}
status = blk.Status()
}
// If [blkID] is currently in consensus, count the votes
if v.t.Consensus.Processing(blkID) {
v.t.Ctx.Log.Verbo("Applying %d vote(s) to %s (%s)", count, blkID, status)
bubbledVotes.AddCount(blkID, count)
} else {
v.t.Ctx.Log.Verbo("Dropping %d vote(s) to %s (%s)", count, blkID, status)
}
}
return bubbledVotes
}