github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowball/network_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package snowball 5 6 import ( 7 "github.com/MetalBlockchain/metalgo/ids" 8 "github.com/MetalBlockchain/metalgo/utils/bag" 9 "github.com/MetalBlockchain/metalgo/utils/sampler" 10 ) 11 12 type newConsensusFunc func(factory Factory, params Parameters, choice ids.ID) Consensus 13 14 type Network struct { 15 params Parameters 16 colors []ids.ID 17 rngSource sampler.Source 18 nodes, running []Consensus 19 factory Factory 20 } 21 22 // Create a new network with [numColors] different possible colors to finalize. 23 func NewNetwork(factory Factory, params Parameters, numColors int, rngSource sampler.Source) *Network { 24 n := &Network{ 25 params: params, 26 rngSource: rngSource, 27 factory: factory, 28 } 29 for i := 0; i < numColors; i++ { 30 n.colors = append(n.colors, ids.Empty.Prefix(uint64(i))) 31 } 32 return n 33 } 34 35 func (n *Network) AddNode(newConsensusFunc newConsensusFunc) Consensus { 36 s := sampler.NewDeterministicUniform(n.rngSource) 37 s.Initialize(uint64(len(n.colors))) 38 indices, _ := s.Sample(len(n.colors)) 39 40 consensus := newConsensusFunc(n.factory, n.params, n.colors[int(indices[0])]) 41 for _, index := range indices[1:] { 42 consensus.Add(n.colors[int(index)]) 43 } 44 45 n.nodes = append(n.nodes, consensus) 46 if !consensus.Finalized() { 47 n.running = append(n.running, consensus) 48 } 49 50 return consensus 51 } 52 53 // AddNodeSpecificColor adds a new consensus instance to the network which will 54 // initially prefer [initialPreference] and additionally adds each of the 55 // specified [options] to consensus. 56 func (n *Network) AddNodeSpecificColor( 57 newConsensusFunc newConsensusFunc, 58 initialPreference int, 59 options []int, 60 ) Consensus { 61 consensus := newConsensusFunc(n.factory, n.params, n.colors[initialPreference]) 62 63 for _, i := range options { 64 consensus.Add(n.colors[i]) 65 } 66 67 n.nodes = append(n.nodes, consensus) 68 if !consensus.Finalized() { 69 n.running = append(n.running, consensus) 70 } 71 72 return consensus 73 } 74 75 // Finalized returns true iff every node added to the network has finished 76 // running. 77 func (n *Network) Finalized() bool { 78 return len(n.running) == 0 79 } 80 81 // Round simulates a round of consensus by randomly selecting a running node and 82 // performing an unbiased poll of the nodes in the network for that node. 83 func (n *Network) Round() { 84 if len(n.running) > 0 { 85 s := sampler.NewDeterministicUniform(n.rngSource) 86 87 s.Initialize(uint64(len(n.running))) 88 runningInd, _ := s.Next() 89 running := n.running[runningInd] 90 91 s.Initialize(uint64(len(n.nodes))) 92 count := min(n.params.K, len(n.nodes)) 93 indices, _ := s.Sample(count) 94 sampledColors := bag.Bag[ids.ID]{} 95 for _, index := range indices { 96 peer := n.nodes[int(index)] 97 sampledColors.Add(peer.Preference()) 98 } 99 100 running.RecordPoll(sampledColors) 101 102 // If this node has been finalized, remove it from the poller 103 if running.Finalized() { 104 newSize := len(n.running) - 1 105 n.running[runningInd] = n.running[newSize] 106 n.running = n.running[:newSize] 107 } 108 } 109 } 110 111 // Disagreement returns true iff there are any two nodes in the network that 112 // have finalized two different preferences. 113 func (n *Network) Disagreement() bool { 114 // Iterate [i] to the index of the first node that has finalized. 115 i := 0 116 for ; i < len(n.nodes) && !n.nodes[i].Finalized(); i++ { 117 } 118 // If none of the nodes have finalized, then there is no disagreement. 119 if i >= len(n.nodes) { 120 return false 121 } 122 123 // Return true if any other finalized node has finalized a different 124 // preference. 125 pref := n.nodes[i].Preference() 126 for ; i < len(n.nodes); i++ { 127 if node := n.nodes[i]; node.Finalized() && pref != node.Preference() { 128 return true 129 } 130 } 131 return false 132 } 133 134 // Agreement returns true iff every node in the network prefers the same value. 135 func (n *Network) Agreement() bool { 136 if len(n.nodes) == 0 { 137 return true 138 } 139 pref := n.nodes[0].Preference() 140 for _, node := range n.nodes { 141 if pref != node.Preference() { 142 return false 143 } 144 } 145 return true 146 }