github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowman/bootstrapper/majority_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 bootstrapper 5 6 import ( 7 "context" 8 "math" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/utils/logging" 15 "github.com/MetalBlockchain/metalgo/utils/set" 16 17 safemath "github.com/MetalBlockchain/metalgo/utils/math" 18 ) 19 20 func TestNewMajority(t *testing.T) { 21 majority := NewMajority( 22 logging.NoLog{}, // log 23 map[ids.NodeID]uint64{ 24 nodeID0: 1, 25 nodeID1: 1, 26 }, // nodeWeights 27 2, // maxOutstanding 28 ) 29 30 expectedMajority := &Majority{ 31 requests: requests{ 32 maxOutstanding: 2, 33 pendingSend: set.Of(nodeID0, nodeID1), 34 }, 35 log: logging.NoLog{}, 36 nodeWeights: map[ids.NodeID]uint64{ 37 nodeID0: 1, 38 nodeID1: 1, 39 }, 40 received: make(map[ids.ID]uint64), 41 } 42 require.Equal(t, expectedMajority, majority) 43 } 44 45 func TestMajorityGetPeers(t *testing.T) { 46 tests := []struct { 47 name string 48 majority Poll 49 expectedState Poll 50 expectedPeers set.Set[ids.NodeID] 51 }{ 52 { 53 name: "max outstanding", 54 majority: &Majority{ 55 requests: requests{ 56 maxOutstanding: 1, 57 pendingSend: set.Of(nodeID0), 58 outstanding: set.Of(nodeID1), 59 }, 60 log: logging.NoLog{}, 61 nodeWeights: map[ids.NodeID]uint64{ 62 nodeID0: 1, 63 nodeID1: 1, 64 }, 65 received: make(map[ids.ID]uint64), 66 }, 67 expectedState: &Majority{ 68 requests: requests{ 69 maxOutstanding: 1, 70 pendingSend: set.Of(nodeID0), 71 outstanding: set.Of(nodeID1), 72 }, 73 log: logging.NoLog{}, 74 nodeWeights: map[ids.NodeID]uint64{ 75 nodeID0: 1, 76 nodeID1: 1, 77 }, 78 received: make(map[ids.ID]uint64), 79 }, 80 expectedPeers: nil, 81 }, 82 { 83 name: "send until max outstanding", 84 majority: &Majority{ 85 requests: requests{ 86 maxOutstanding: 2, 87 pendingSend: set.Of(nodeID0, nodeID1), 88 }, 89 log: logging.NoLog{}, 90 nodeWeights: map[ids.NodeID]uint64{ 91 nodeID0: 1, 92 nodeID1: 1, 93 }, 94 received: make(map[ids.ID]uint64), 95 }, 96 expectedState: &Majority{ 97 requests: requests{ 98 maxOutstanding: 2, 99 pendingSend: set.Set[ids.NodeID]{}, 100 outstanding: set.Of(nodeID0, nodeID1), 101 }, 102 log: logging.NoLog{}, 103 nodeWeights: map[ids.NodeID]uint64{ 104 nodeID0: 1, 105 nodeID1: 1, 106 }, 107 received: make(map[ids.ID]uint64), 108 }, 109 expectedPeers: set.Of(nodeID0, nodeID1), 110 }, 111 { 112 name: "send until no more to send", 113 majority: &Majority{ 114 requests: requests{ 115 maxOutstanding: 2, 116 pendingSend: set.Of(nodeID0), 117 }, 118 log: logging.NoLog{}, 119 nodeWeights: map[ids.NodeID]uint64{ 120 nodeID0: 1, 121 }, 122 received: make(map[ids.ID]uint64), 123 }, 124 expectedState: &Majority{ 125 requests: requests{ 126 maxOutstanding: 2, 127 pendingSend: set.Set[ids.NodeID]{}, 128 outstanding: set.Of(nodeID0), 129 }, 130 log: logging.NoLog{}, 131 nodeWeights: map[ids.NodeID]uint64{ 132 nodeID0: 1, 133 }, 134 received: make(map[ids.ID]uint64), 135 }, 136 expectedPeers: set.Of(nodeID0), 137 }, 138 } 139 for _, test := range tests { 140 t.Run(test.name, func(t *testing.T) { 141 require := require.New(t) 142 143 peers := test.majority.GetPeers(context.Background()) 144 require.Equal(test.expectedState, test.majority) 145 require.Equal(test.expectedPeers, peers) 146 }) 147 } 148 } 149 150 func TestMajorityRecordOpinion(t *testing.T) { 151 tests := []struct { 152 name string 153 majority Poll 154 nodeID ids.NodeID 155 blkIDs set.Set[ids.ID] 156 expectedState Poll 157 expectedErr error 158 }{ 159 { 160 name: "unexpected response", 161 majority: &Majority{ 162 requests: requests{ 163 maxOutstanding: 1, 164 pendingSend: set.Of(nodeID0), 165 outstanding: set.Of(nodeID1), 166 }, 167 log: logging.NoLog{}, 168 nodeWeights: map[ids.NodeID]uint64{ 169 nodeID0: 1, 170 nodeID1: 1, 171 }, 172 received: make(map[ids.ID]uint64), 173 }, 174 nodeID: nodeID0, 175 blkIDs: nil, 176 expectedState: &Majority{ 177 requests: requests{ 178 maxOutstanding: 1, 179 pendingSend: set.Of(nodeID0), 180 outstanding: set.Of(nodeID1), 181 }, 182 log: logging.NoLog{}, 183 nodeWeights: map[ids.NodeID]uint64{ 184 nodeID0: 1, 185 nodeID1: 1, 186 }, 187 received: make(map[ids.ID]uint64), 188 }, 189 expectedErr: nil, 190 }, 191 { 192 name: "unfinished after response", 193 majority: &Majority{ 194 requests: requests{ 195 maxOutstanding: 1, 196 pendingSend: set.Of(nodeID0), 197 outstanding: set.Of(nodeID1), 198 }, 199 log: logging.NoLog{}, 200 nodeWeights: map[ids.NodeID]uint64{ 201 nodeID0: 2, 202 nodeID1: 3, 203 }, 204 received: make(map[ids.ID]uint64), 205 }, 206 nodeID: nodeID1, 207 blkIDs: set.Of(blkID0), 208 expectedState: &Majority{ 209 requests: requests{ 210 maxOutstanding: 1, 211 pendingSend: set.Of(nodeID0), 212 outstanding: set.Set[ids.NodeID]{}, 213 }, 214 log: logging.NoLog{}, 215 nodeWeights: map[ids.NodeID]uint64{ 216 nodeID0: 2, 217 nodeID1: 3, 218 }, 219 received: map[ids.ID]uint64{ 220 blkID0: 3, 221 }, 222 }, 223 expectedErr: nil, 224 }, 225 { 226 name: "overflow during response", 227 majority: &Majority{ 228 requests: requests{ 229 maxOutstanding: 1, 230 outstanding: set.Of(nodeID1), 231 }, 232 log: logging.NoLog{}, 233 nodeWeights: map[ids.NodeID]uint64{ 234 nodeID0: 1, 235 nodeID1: math.MaxUint64, 236 }, 237 received: map[ids.ID]uint64{ 238 blkID0: 1, 239 }, 240 }, 241 nodeID: nodeID1, 242 blkIDs: set.Of(blkID0), 243 expectedState: &Majority{ 244 requests: requests{ 245 maxOutstanding: 1, 246 outstanding: set.Set[ids.NodeID]{}, 247 }, 248 log: logging.NoLog{}, 249 nodeWeights: map[ids.NodeID]uint64{ 250 nodeID0: 1, 251 nodeID1: math.MaxUint64, 252 }, 253 received: map[ids.ID]uint64{ 254 blkID0: 1, 255 }, 256 }, 257 expectedErr: safemath.ErrOverflow, 258 }, 259 { 260 name: "overflow during final response", 261 majority: &Majority{ 262 requests: requests{ 263 maxOutstanding: 1, 264 outstanding: set.Of(nodeID1), 265 }, 266 log: logging.NoLog{}, 267 nodeWeights: map[ids.NodeID]uint64{ 268 nodeID0: 1, 269 nodeID1: math.MaxUint64, 270 }, 271 received: make(map[ids.ID]uint64), 272 }, 273 nodeID: nodeID1, 274 blkIDs: set.Of(blkID0), 275 expectedState: &Majority{ 276 requests: requests{ 277 maxOutstanding: 1, 278 outstanding: set.Set[ids.NodeID]{}, 279 }, 280 log: logging.NoLog{}, 281 nodeWeights: map[ids.NodeID]uint64{ 282 nodeID0: 1, 283 nodeID1: math.MaxUint64, 284 }, 285 received: map[ids.ID]uint64{ 286 blkID0: math.MaxUint64, 287 }, 288 }, 289 expectedErr: safemath.ErrOverflow, 290 }, 291 { 292 name: "finished after response", 293 majority: &Majority{ 294 requests: requests{ 295 maxOutstanding: 1, 296 outstanding: set.Of(nodeID2), 297 }, 298 log: logging.NoLog{}, 299 nodeWeights: map[ids.NodeID]uint64{ 300 nodeID0: 1, 301 nodeID1: 1, 302 nodeID2: 1, 303 }, 304 received: map[ids.ID]uint64{ 305 blkID0: 1, 306 blkID1: 1, 307 }, 308 }, 309 nodeID: nodeID2, 310 blkIDs: set.Of(blkID1), 311 expectedState: &Majority{ 312 requests: requests{ 313 maxOutstanding: 1, 314 outstanding: set.Set[ids.NodeID]{}, 315 }, 316 log: logging.NoLog{}, 317 nodeWeights: map[ids.NodeID]uint64{ 318 nodeID0: 1, 319 nodeID1: 1, 320 nodeID2: 1, 321 }, 322 received: map[ids.ID]uint64{ 323 blkID0: 1, 324 blkID1: 2, 325 }, 326 accepted: []ids.ID{blkID1}, 327 }, 328 expectedErr: nil, 329 }, 330 } 331 for _, test := range tests { 332 t.Run(test.name, func(t *testing.T) { 333 require := require.New(t) 334 335 err := test.majority.RecordOpinion(context.Background(), test.nodeID, test.blkIDs) 336 require.Equal(test.expectedState, test.majority) 337 require.ErrorIs(err, test.expectedErr) 338 }) 339 } 340 } 341 342 func TestMajorityResult(t *testing.T) { 343 tests := []struct { 344 name string 345 majority Poll 346 expectedAccepted []ids.ID 347 expectedFinalized bool 348 }{ 349 { 350 name: "not finalized", 351 majority: &Majority{ 352 requests: requests{ 353 maxOutstanding: 1, 354 outstanding: set.Of(nodeID1), 355 }, 356 log: logging.NoLog{}, 357 nodeWeights: map[ids.NodeID]uint64{ 358 nodeID0: 1, 359 nodeID1: 1, 360 }, 361 received: make(map[ids.ID]uint64), 362 accepted: nil, 363 }, 364 expectedAccepted: nil, 365 expectedFinalized: false, 366 }, 367 { 368 name: "finalized", 369 majority: &Majority{ 370 requests: requests{ 371 maxOutstanding: 1, 372 }, 373 log: logging.NoLog{}, 374 nodeWeights: map[ids.NodeID]uint64{ 375 nodeID0: 1, 376 nodeID1: 1, 377 }, 378 received: map[ids.ID]uint64{ 379 blkID0: 2, 380 }, 381 accepted: []ids.ID{blkID0}, 382 }, 383 expectedAccepted: []ids.ID{blkID0}, 384 expectedFinalized: true, 385 }, 386 } 387 for _, test := range tests { 388 t.Run(test.name, func(t *testing.T) { 389 require := require.New(t) 390 391 accepted, finalized := test.majority.Result(context.Background()) 392 require.Equal(test.expectedAccepted, accepted) 393 require.Equal(test.expectedFinalized, finalized) 394 }) 395 } 396 }