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  }