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  }