From 6339a115dc95c11cbd8b8968d2c836e12cc6a3c2 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Thu, 4 Mar 2021 18:38:08 +0300 Subject: [PATCH] [#421] governance: Add new alphabet list constructor Signed-off-by: Alex Vanin --- pkg/innerring/processors/governance/list.go | 64 ++++++++++++++ .../processors/governance/list_test.go | 84 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 pkg/innerring/processors/governance/list.go create mode 100644 pkg/innerring/processors/governance/list_test.go diff --git a/pkg/innerring/processors/governance/list.go b/pkg/innerring/processors/governance/list.go new file mode 100644 index 00000000..4babe930 --- /dev/null +++ b/pkg/innerring/processors/governance/list.go @@ -0,0 +1,64 @@ +package governance + +import ( + "sort" + + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/pkg/errors" +) + +var errNotEnoughKeys = errors.New("alphabet list in mainnet is too short") + +// newAlphabetList returns updated list of sidechain keys with no more than 1\3 +// of new keys from mainnet list. Function returns `errNotEnoughKeys` if +// mainnet list contains less keys than sidechain list. Function returns +// (nil, nil) if mainnet list contains all keys from sidechain list. +func newAlphabetList(sidechain, mainnet keys.PublicKeys) (keys.PublicKeys, error) { + ln := len(sidechain) + if len(mainnet) < ln { + return nil, errors.Wrapf(errNotEnoughKeys, "expecting %d keys", ln) + } + + hmap := make(map[string]bool, ln) + result := make(keys.PublicKeys, 0, ln) + + for _, node := range sidechain { + hmap[node.Address()] = false + } + + newNodes := 0 + newNodeLimit := (ln - 1) / 3 + + for i := 0; i < ln; i++ { + if newNodes == newNodeLimit { + break + } + + mainnetAddr := mainnet[i].Address() + if _, ok := hmap[mainnetAddr]; !ok { + newNodes++ + } else { + hmap[mainnetAddr] = true + } + + result = append(result, mainnet[i]) + } + + if newNodes == 0 { + return nil, nil + } + + for _, node := range sidechain { + if len(result) == ln { + break + } + + if !hmap[node.Address()] { + result = append(result, node) + } + } + + sort.Sort(result) + + return result, nil +} diff --git a/pkg/innerring/processors/governance/list_test.go b/pkg/innerring/processors/governance/list_test.go new file mode 100644 index 00000000..d249ac22 --- /dev/null +++ b/pkg/innerring/processors/governance/list_test.go @@ -0,0 +1,84 @@ +package governance + +import ( + "sort" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/stretchr/testify/require" +) + +func TestNewAlphabetList(t *testing.T) { + k, err := generateKeys(14) + require.NoError(t, err) + + orig := keys.PublicKeys{k[0], k[1], k[2], k[3], k[4], k[5], k[6]} + + t.Run("same keys", func(t *testing.T) { + list, err := newAlphabetList(orig, orig) + require.NoError(t, err) + require.Nil(t, list) + }) + + t.Run("not enough mainnet keys", func(t *testing.T) { + _, err := newAlphabetList(orig, orig[:len(orig)-1]) + require.Error(t, err) + }) + + t.Run("less than third new keys", func(t *testing.T) { + exp := keys.PublicKeys{k[1], k[2], k[3], k[4], k[5], k[6], k[7]} + got, err := newAlphabetList(orig, exp) + require.NoError(t, err) + require.True(t, equalPublicKeyLists(exp, got)) + }) + + t.Run("completely new list of keys", func(t *testing.T) { + list := orig + exp := keys.PublicKeys{k[7], k[8], k[9], k[10], k[11], k[12], k[13]} + + rounds := []keys.PublicKeys{ + {k[0], k[1], k[2], k[3], k[4], k[7], k[8]}, + {k[0], k[1], k[2], k[7], k[8], k[9], k[10]}, + {k[0], k[7], k[8], k[9], k[10], k[11], k[12]}, + exp, + } + ln := len(rounds) + + for i := 0; i < ln; i++ { + list, err = newAlphabetList(list, exp) + require.NoError(t, err) + require.True(t, equalPublicKeyLists(list, rounds[i])) + } + }) +} + +func generateKeys(n int) (keys.PublicKeys, error) { + pubKeys := make(keys.PublicKeys, 0, n) + + for i := 0; i < n; i++ { + privKey, err := keys.NewPrivateKey() + if err != nil { + return nil, err + } + + pubKeys = append(pubKeys, privKey.PublicKey()) + } + + sort.Sort(pubKeys) + + return pubKeys, nil +} + +func equalPublicKeyLists(a, b keys.PublicKeys) bool { + if len(a) != len(b) { + return false + } + + for i, node := range a { + if !b[i].Equal(node) { + return false + } + } + + return true +}