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  }