2020-03-10 12:20:34 -07:00
|
|
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
|
|
|
|
package snowman
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
|
|
"github.com/ava-labs/gecko/snow"
|
|
|
|
"github.com/ava-labs/gecko/snow/choices"
|
|
|
|
"github.com/ava-labs/gecko/snow/consensus/snowball"
|
|
|
|
"github.com/ava-labs/gecko/utils/random"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Network struct {
|
|
|
|
params snowball.Parameters
|
2020-04-04 15:07:14 -07:00
|
|
|
colors []*TestBlock
|
2020-03-10 12:20:34 -07:00
|
|
|
nodes, running []Consensus
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Network) shuffleColors() {
|
|
|
|
s := random.Uniform{N: len(n.colors)}
|
2020-04-04 15:07:14 -07:00
|
|
|
colors := []*TestBlock(nil)
|
2020-03-10 12:20:34 -07:00
|
|
|
for s.CanSample() {
|
|
|
|
colors = append(colors, n.colors[s.Sample()])
|
|
|
|
}
|
|
|
|
n.colors = colors
|
|
|
|
SortVts(n.colors)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Network) Initialize(params snowball.Parameters, numColors int) {
|
|
|
|
n.params = params
|
2020-04-04 15:07:14 -07:00
|
|
|
n.colors = append(n.colors, &TestBlock{
|
2020-03-10 12:20:34 -07:00
|
|
|
parent: Genesis,
|
|
|
|
id: ids.Empty.Prefix(uint64(random.Rand(0, math.MaxInt64))),
|
|
|
|
status: choices.Processing,
|
|
|
|
})
|
|
|
|
|
|
|
|
for i := 1; i < numColors; i++ {
|
|
|
|
dependency := n.colors[random.Rand(0, len(n.colors))]
|
2020-04-04 15:07:14 -07:00
|
|
|
n.colors = append(n.colors, &TestBlock{
|
2020-03-10 12:20:34 -07:00
|
|
|
parent: dependency,
|
|
|
|
id: ids.Empty.Prefix(uint64(random.Rand(0, math.MaxInt64))),
|
|
|
|
height: dependency.height + 1,
|
|
|
|
status: choices.Processing,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Network) AddNode(sm Consensus) {
|
|
|
|
sm.Initialize(snow.DefaultContextTest(), n.params, Genesis.ID())
|
|
|
|
|
|
|
|
n.shuffleColors()
|
|
|
|
deps := map[[32]byte]Block{}
|
|
|
|
for _, blk := range n.colors {
|
|
|
|
myDep, found := deps[blk.parent.ID().Key()]
|
|
|
|
if !found {
|
|
|
|
myDep = blk.parent
|
|
|
|
}
|
2020-04-04 15:07:14 -07:00
|
|
|
myVtx := &TestBlock{
|
2020-03-10 12:20:34 -07:00
|
|
|
parent: myDep,
|
|
|
|
id: blk.id,
|
|
|
|
height: blk.height,
|
|
|
|
status: blk.status,
|
|
|
|
}
|
|
|
|
sm.Add(myVtx)
|
|
|
|
deps[myVtx.ID().Key()] = myDep
|
|
|
|
}
|
|
|
|
n.nodes = append(n.nodes, sm)
|
|
|
|
n.running = append(n.running, sm)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Network) Finalized() bool { return len(n.running) == 0 }
|
|
|
|
|
|
|
|
func (n *Network) Round() {
|
|
|
|
if len(n.running) > 0 {
|
|
|
|
runningInd := random.Rand(0, len(n.running))
|
|
|
|
running := n.running[runningInd]
|
|
|
|
|
|
|
|
sampler := random.Uniform{N: len(n.nodes)}
|
|
|
|
sampledColors := ids.Bag{}
|
|
|
|
for i := 0; i < n.params.K; i++ {
|
|
|
|
peer := n.nodes[sampler.Sample()]
|
|
|
|
if peer != running {
|
|
|
|
sampledColors.Add(peer.Preference())
|
|
|
|
} else {
|
|
|
|
i-- // So that we still sample k people
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
running.RecordPoll(sampledColors)
|
|
|
|
|
|
|
|
// If this node has been finalized, remove it from the poller
|
|
|
|
if running.Finalized() {
|
|
|
|
newSize := len(n.running) - 1
|
|
|
|
n.running[runningInd] = n.running[newSize]
|
|
|
|
n.running = n.running[:newSize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Network) Agreement() bool {
|
|
|
|
if len(n.nodes) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
pref := n.nodes[0].Preference()
|
|
|
|
for _, node := range n.nodes {
|
|
|
|
if !pref.Equals(node.Preference()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|