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