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