github.com/onflow/flow-go@v0.33.17/model/flow/identity_test.go (about) 1 package flow_test 2 3 import ( 4 "encoding/json" 5 "strings" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 "github.com/vmihailenco/msgpack/v4" 11 12 "github.com/onflow/flow-go/crypto" 13 "github.com/onflow/flow-go/model/encodable" 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/utils/unittest" 16 ) 17 18 func TestHexStringToIdentifier(t *testing.T) { 19 type testcase struct { 20 hex string 21 expectError bool 22 } 23 24 cases := []testcase{{ 25 // non-hex characters 26 hex: "123456789012345678901234567890123456789012345678901234567890123z", 27 expectError: true, 28 }, { 29 // too short 30 hex: "1234", 31 expectError: true, 32 }, { 33 // just right 34 hex: "1234567890123456789012345678901234567890123456789012345678901234", 35 expectError: false, 36 }} 37 38 for _, tcase := range cases { 39 id, err := flow.HexStringToIdentifier(tcase.hex) 40 if tcase.expectError { 41 assert.Error(t, err) 42 continue 43 } else { 44 assert.NoError(t, err) 45 } 46 47 assert.Equal(t, tcase.hex, id.String()) 48 } 49 } 50 51 func TestIdentityEncodingJSON(t *testing.T) { 52 53 t.Run("normal identity", func(t *testing.T) { 54 identity := unittest.IdentityFixture(unittest.WithRandomPublicKeys()) 55 enc, err := json.Marshal(identity) 56 require.NoError(t, err) 57 var dec flow.Identity 58 err = json.Unmarshal(enc, &dec) 59 require.NoError(t, err) 60 require.True(t, identity.Equals(&dec)) 61 }) 62 63 t.Run("empty address should be omitted", func(t *testing.T) { 64 identity := unittest.IdentityFixture(unittest.WithRandomPublicKeys()) 65 identity.Address = "" 66 enc, err := json.Marshal(identity) 67 require.NoError(t, err) 68 // should have no address field in output 69 assert.False(t, strings.Contains(string(enc), "Address")) 70 var dec flow.Identity 71 err = json.Unmarshal(enc, &dec) 72 require.NoError(t, err) 73 require.True(t, identity.Equals(&dec)) 74 }) 75 76 t.Run("compat: should accept old files using Stake field", func(t *testing.T) { 77 identity := unittest.IdentityFixture(unittest.WithRandomPublicKeys()) 78 enc, err := json.Marshal(identity) 79 require.NoError(t, err) 80 // emulate the old encoding by replacing the new field with old field name 81 enc = []byte(strings.Replace(string(enc), "Weight", "Stake", 1)) 82 var dec flow.Identity 83 err = json.Unmarshal(enc, &dec) 84 require.NoError(t, err) 85 require.True(t, identity.Equals(&dec)) 86 }) 87 } 88 89 func TestIdentityEncodingMsgpack(t *testing.T) { 90 identity := unittest.IdentityFixture(unittest.WithRandomPublicKeys()) 91 enc, err := msgpack.Marshal(identity) 92 require.NoError(t, err) 93 var dec flow.Identity 94 err = msgpack.Unmarshal(enc, &dec) 95 require.NoError(t, err) 96 require.True(t, identity.Equals(&dec)) 97 } 98 99 func TestIdentityList_Exists(t *testing.T) { 100 t.Run("should find a given element", func(t *testing.T) { 101 il1 := unittest.IdentityListFixture(10) 102 il2 := unittest.IdentityListFixture(1) 103 104 // sort the first list 105 il1 = il1.Sort(flow.Canonical) 106 107 for i := 0; i < 10; i++ { 108 assert.True(t, il1.Exists(il1[i])) 109 } 110 assert.False(t, il1.Exists(il2[0])) 111 }) 112 } 113 114 func TestIdentityList_IdentifierExists(t *testing.T) { 115 t.Run("should find a given identifier", func(t *testing.T) { 116 il1 := unittest.IdentityListFixture(10) 117 il2 := unittest.IdentityListFixture(1) 118 119 // sort the first list 120 il1 = il1.Sort(flow.Canonical) 121 122 for i := 0; i < 10; i++ { 123 assert.True(t, il1.IdentifierExists(il1[i].NodeID)) 124 } 125 assert.False(t, il1.IdentifierExists(il2[0].NodeID)) 126 }) 127 } 128 129 func TestIdentityList_Union(t *testing.T) { 130 t.Run("retains the original identity list", func(t *testing.T) { 131 // An identity list is a slice, i.e. it is vulnerable to in-place modifications via append. 132 // Per convention, all IdentityList operations should leave the original lists invariant. 133 // Here, we purposefully create a slice, whose backing array has enough space to 134 // include the elements we are going to add via Union. Furthermore, we force element duplication 135 // by taking the union of the IdentityList with itself. If the implementation is not careful 136 // about creating copies and works with the slices itself, it will modify the input and fail the test. 137 138 il := unittest.IdentityListFixture(20) 139 il = il[:10] 140 ilBackup := il.Copy() 141 142 _ = il.Union(il) 143 assert.Equal(t, ilBackup, il) 144 }) 145 t.Run("should contain all identities", func(t *testing.T) { 146 il1 := unittest.IdentityListFixture(10) 147 il2 := unittest.IdentityListFixture(10) 148 149 union := il1.Union(il2) 150 151 uniques := make(map[flow.Identifier]struct{}) 152 153 // should contain all items form 1 and 2, since there are no duplicates 154 assert.Len(t, union, len(il1)+len(il2)) 155 for _, identity := range union { 156 _, in1 := il1.ByNodeID(identity.NodeID) 157 _, in2 := il2.ByNodeID(identity.NodeID) 158 // each item should be in one of the input lists 159 assert.True(t, in1 || in2) 160 161 // there should be no duplicates 162 _, dupe := uniques[identity.NodeID] 163 assert.False(t, dupe) 164 uniques[identity.NodeID] = struct{}{} 165 } 166 }) 167 168 t.Run("should omit duplicates", func(t *testing.T) { 169 il1 := unittest.IdentityListFixture(10) 170 il2 := unittest.IdentityListFixture(10) 171 // add one duplicate between the two lists, which should be included only once 172 dup := il1[0] 173 il2[0] = dup 174 175 union := il1.Union(il2) 176 177 uniques := make(map[flow.Identifier]struct{}) 178 179 // should contain one less than the sum of the two input list lengths since there is a dupe 180 assert.Len(t, union, len(il1)+len(il2)-1) 181 for _, identity := range union { 182 _, in1 := il1.ByNodeID(identity.NodeID) 183 _, in2 := il2.ByNodeID(identity.NodeID) 184 // each item should be in one of the input lists 185 assert.True(t, in1 || in2) 186 187 // there should be no duplicates 188 _, dupe := uniques[identity.NodeID] 189 assert.False(t, dupe) 190 uniques[identity.NodeID] = struct{}{} 191 } 192 }) 193 } 194 195 func TestSample(t *testing.T) { 196 t.Run("Sample max", func(t *testing.T) { 197 il := unittest.IdentityListFixture(10) 198 sam, err := il.Sample(10) 199 require.NoError(t, err) 200 require.Equal(t, uint(10), sam.Count()) 201 }) 202 203 t.Run("Sample oversized", func(t *testing.T) { 204 il := unittest.IdentityListFixture(10) 205 sam, err := il.Sample(11) 206 require.NoError(t, err) 207 require.Equal(t, uint(10), sam.Count()) 208 }) 209 } 210 211 func TestShuffle(t *testing.T) { 212 t.Run("should be shuffled", func(t *testing.T) { 213 il := unittest.IdentityListFixture(15) // ~1/billion chance of shuffling to input state 214 shuffled, err := il.Shuffle() 215 require.NoError(t, err) 216 assert.Equal(t, len(il), len(shuffled)) 217 assert.ElementsMatch(t, il, shuffled) 218 }) 219 t.Run("should not be deterministic", func(t *testing.T) { 220 il := unittest.IdentityListFixture(10) 221 shuffled1, err := il.Shuffle() 222 require.NoError(t, err) 223 shuffled2, err := il.Shuffle() 224 require.NoError(t, err) 225 assert.NotEqual(t, shuffled1, shuffled2) 226 assert.ElementsMatch(t, shuffled1, shuffled2) 227 }) 228 } 229 230 // check that identities consistently hash to the same ID, even with different 231 // public key implementations 232 func TestIdentity_ID(t *testing.T) { 233 identity1 := unittest.IdentityFixture(unittest.WithKeys) 234 var identity2 = new(flow.Identity) 235 *identity2 = *identity1 236 identity2.StakingPubKey = encodable.StakingPubKey{PublicKey: identity1.StakingPubKey} 237 238 id1 := flow.MakeID(identity1) 239 id2 := flow.MakeID(identity2) 240 assert.Equal(t, id1, id2) 241 } 242 243 func TestIdentity_Sort(t *testing.T) { 244 il := unittest.IdentityListFixture(20) 245 // make sure the list is not sorted 246 il[0].NodeID[0], il[1].NodeID[0] = 2, 1 247 require.False(t, flow.IsCanonical(il[0], il[1])) 248 assert.False(t, flow.IsIdentityListCanonical(il)) 249 250 canonical := il.Sort(flow.Canonical) 251 assert.True(t, flow.IsIdentityListCanonical(canonical)) 252 253 // check `IsIdentityListCanonical` detects order equality in a sorted list 254 il[1] = il[10] // add a duplication 255 canonical = il.Sort(flow.Canonical) 256 assert.False(t, flow.IsIdentityListCanonical(canonical)) 257 } 258 259 func TestIdentity_EqualTo(t *testing.T) { 260 261 pks := unittest.PublicKeysFixture(2, crypto.ECDSASecp256k1) 262 263 t.Run("empty are equal", func(t *testing.T) { 264 a := &flow.Identity{} 265 b := &flow.Identity{} 266 267 require.True(t, a.EqualTo(b)) 268 require.True(t, b.EqualTo(a)) 269 }) 270 271 t.Run("NodeID diff", func(t *testing.T) { 272 a := &flow.Identity{NodeID: [32]byte{1, 2, 3}} 273 b := &flow.Identity{NodeID: [32]byte{2, 2, 2}} 274 275 require.False(t, a.EqualTo(b)) 276 require.False(t, b.EqualTo(a)) 277 }) 278 279 t.Run("Address diff", func(t *testing.T) { 280 a := &flow.Identity{Address: "b"} 281 b := &flow.Identity{Address: "c"} 282 283 require.False(t, a.EqualTo(b)) 284 require.False(t, b.EqualTo(a)) 285 }) 286 287 t.Run("Role diff", func(t *testing.T) { 288 a := &flow.Identity{Role: flow.RoleCollection} 289 b := &flow.Identity{Role: flow.RoleExecution} 290 291 require.False(t, a.EqualTo(b)) 292 require.False(t, b.EqualTo(a)) 293 }) 294 295 t.Run("Weight diff", func(t *testing.T) { 296 a := &flow.Identity{Weight: 1} 297 b := &flow.Identity{Weight: 2} 298 299 require.False(t, a.EqualTo(b)) 300 require.False(t, b.EqualTo(a)) 301 }) 302 303 t.Run("Ejected diff", func(t *testing.T) { 304 a := &flow.Identity{Ejected: true} 305 b := &flow.Identity{Ejected: false} 306 307 require.False(t, a.EqualTo(b)) 308 require.False(t, b.EqualTo(a)) 309 }) 310 311 t.Run("StakingPubKey diff", func(t *testing.T) { 312 a := &flow.Identity{StakingPubKey: pks[0]} 313 b := &flow.Identity{StakingPubKey: pks[1]} 314 315 require.False(t, a.EqualTo(b)) 316 require.False(t, b.EqualTo(a)) 317 }) 318 319 t.Run("NetworkPubKey diff", func(t *testing.T) { 320 a := &flow.Identity{NetworkPubKey: pks[0]} 321 b := &flow.Identity{NetworkPubKey: pks[1]} 322 323 require.False(t, a.EqualTo(b)) 324 require.False(t, b.EqualTo(a)) 325 }) 326 327 t.Run("Same data equals", func(t *testing.T) { 328 a := &flow.Identity{ 329 NodeID: flow.Identifier{1, 2, 3}, 330 Address: "address", 331 Role: flow.RoleCollection, 332 Weight: 23, 333 Ejected: false, 334 StakingPubKey: pks[0], 335 NetworkPubKey: pks[1], 336 } 337 b := &flow.Identity{ 338 NodeID: flow.Identifier{1, 2, 3}, 339 Address: "address", 340 Role: flow.RoleCollection, 341 Weight: 23, 342 Ejected: false, 343 StakingPubKey: pks[0], 344 NetworkPubKey: pks[1], 345 } 346 347 require.True(t, a.EqualTo(b)) 348 require.True(t, b.EqualTo(a)) 349 }) 350 } 351 352 func TestIdentityList_EqualTo(t *testing.T) { 353 354 t.Run("empty are equal", func(t *testing.T) { 355 a := flow.IdentityList{} 356 b := flow.IdentityList{} 357 358 require.True(t, a.EqualTo(b)) 359 require.True(t, b.EqualTo(a)) 360 }) 361 362 t.Run("different len arent equal", func(t *testing.T) { 363 identityA := unittest.IdentityFixture() 364 365 a := flow.IdentityList{identityA} 366 b := flow.IdentityList{} 367 368 require.False(t, a.EqualTo(b)) 369 require.False(t, b.EqualTo(a)) 370 }) 371 372 t.Run("different data means not equal", func(t *testing.T) { 373 identityA := unittest.IdentityFixture() 374 identityB := unittest.IdentityFixture() 375 376 a := flow.IdentityList{identityA} 377 b := flow.IdentityList{identityB} 378 379 require.False(t, a.EqualTo(b)) 380 require.False(t, b.EqualTo(a)) 381 }) 382 383 t.Run("same data means equal", func(t *testing.T) { 384 identityA := unittest.IdentityFixture() 385 386 a := flow.IdentityList{identityA, identityA} 387 b := flow.IdentityList{identityA, identityA} 388 389 require.True(t, a.EqualTo(b)) 390 require.True(t, b.EqualTo(a)) 391 }) 392 } 393 394 func TestIdentityList_GetIndex(t *testing.T) { 395 t.Run("should return expected index of identifier in identity list and true", func(t *testing.T) { 396 participants := unittest.IdentityListFixture(3) 397 index, ok := participants.GetIndex(participants[1].NodeID) 398 require.True(t, ok) 399 require.Equal(t, uint(1), index) 400 }) 401 402 t.Run("should return 0 and false for identifier not found in identity list", func(t *testing.T) { 403 participants := unittest.IdentityListFixture(3) 404 index, ok := participants.GetIndex(unittest.IdentifierFixture()) 405 require.False(t, ok) 406 require.Equal(t, uint(0), index) 407 }) 408 }