diff --git a/pkg/innerring/processors/governance/handlers_test.go b/pkg/innerring/processors/governance/handlers_test.go new file mode 100644 index 000000000..23f91869a --- /dev/null +++ b/pkg/innerring/processors/governance/handlers_test.go @@ -0,0 +1,304 @@ +package governance + +import ( + "encoding/binary" + "sort" + "testing" + "time" + + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" + frostfscontract "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfs" + nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/rolemanagement" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test" + "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestHandleAlphabetSyncEvent(t *testing.T) { + t.Parallel() + testKeys := generateTestKeys(t) + + es := &testEpochState{ + epoch: 100, + } + as := &testAlphabetState{ + isAlphabet: true, + } + v := &testVoter{} + irf := &testIRFetcher{ + publicKeys: testKeys.sidechainKeys, + } + m := &testMorphClient{ + commiteeKeys: testKeys.sidechainKeys, + } + mn := &testMainnetClient{ + alphabetKeys: testKeys.mainnetKeys, + } + f := &testFrostFSClient{} + nm := &testNetmapClient{} + + proc, err := New( + &Params{ + Log: test.NewLogger(t, true), + EpochState: es, + AlphabetState: as, + Voter: v, + IRFetcher: irf, + NotaryDisabled: true, + MorphClient: m, + MainnetClient: mn, + FrostFSClient: f, + NetmapClient: nm, + }, + ) + + require.NoError(t, err, "failed to create processor") + + ev := Sync{ + txHash: util.Uint256{100}, + } + + proc.HandleAlphabetSync(ev) + + for proc.pool.Running() > 0 { + time.Sleep(10 * time.Millisecond) + } + + require.EqualValues(t, []VoteValidatorPrm{ + { + Validators: testKeys.newAlphabetExp, + Hash: &ev.txHash, + }, + }, v.votes, "invalid vote calls") + + var irUpdateExp nmClient.UpdateIRPrm + irUpdateExp.SetKeys(testKeys.newInnerRingExp) + irUpdateExp.SetHash(ev.txHash) + + require.EqualValues(t, []nmClient.UpdateIRPrm{irUpdateExp}, nm.updates, "invalid IR updates") + + var expAlphabetUpdates []client.UpdateAlphabetListPrm + require.EqualValues(t, expAlphabetUpdates, m.alphabetUpdates, "invalid alphabet updates") + + var expNotaryUpdates []client.UpdateNotaryListPrm + require.EqualValues(t, expNotaryUpdates, m.notaryUpdates, "invalid notary list updates") + + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, es.epoch) + + id := append([]byte(alphabetUpdateIDPrefix), buf...) + var expFrostFSAlphabetUpd frostfscontract.AlphabetUpdatePrm + expFrostFSAlphabetUpd.SetID(id) + expFrostFSAlphabetUpd.SetPubs(testKeys.newAlphabetExp) + + require.EqualValues(t, []frostfscontract.AlphabetUpdatePrm{expFrostFSAlphabetUpd}, f.updates, "invalid FrostFS alphabet updates") +} + +func TestHandleAlphabetDesignateEvent(t *testing.T) { + t.Parallel() + testKeys := generateTestKeys(t) + + es := &testEpochState{ + epoch: 100, + } + as := &testAlphabetState{ + isAlphabet: true, + } + v := &testVoter{} + irf := &testIRFetcher{ + publicKeys: testKeys.sidechainKeys, + } + m := &testMorphClient{ + commiteeKeys: testKeys.sidechainKeys, + } + mn := &testMainnetClient{ + alphabetKeys: testKeys.mainnetKeys, + } + f := &testFrostFSClient{} + nm := &testNetmapClient{} + + proc, err := New( + &Params{ + Log: test.NewLogger(t, true), + EpochState: es, + AlphabetState: as, + Voter: v, + IRFetcher: irf, + NotaryDisabled: false, + MorphClient: m, + MainnetClient: mn, + FrostFSClient: f, + NetmapClient: nm, + }, + ) + + require.NoError(t, err, "failed to create processor") + + ev := rolemanagement.Designate{ + TxHash: util.Uint256{100}, + Role: noderoles.NeoFSAlphabet, + } + + proc.HandleAlphabetSync(ev) + + for proc.pool.Running() > 0 { + time.Sleep(10 * time.Millisecond) + } + + require.EqualValues(t, []VoteValidatorPrm{ + { + Validators: testKeys.newAlphabetExp, + Hash: &ev.TxHash, + }, + }, v.votes, "invalid vote calls") + + var irUpdatesExp []nmClient.UpdateIRPrm + require.EqualValues(t, irUpdatesExp, nm.updates, "invalid IR updates") + + var alpabetUpdExp client.UpdateAlphabetListPrm + alpabetUpdExp.SetList(testKeys.newInnerRingExp) + alpabetUpdExp.SetHash(ev.TxHash) + require.EqualValues(t, []client.UpdateAlphabetListPrm{alpabetUpdExp}, m.alphabetUpdates, "invalid alphabet updates") + + var expNotaryUpdate client.UpdateNotaryListPrm + expNotaryUpdate.SetList(testKeys.newAlphabetExp) + expNotaryUpdate.SetHash(ev.TxHash) + require.EqualValues(t, []client.UpdateNotaryListPrm{expNotaryUpdate}, m.notaryUpdates, "invalid notary list updates") + + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, es.epoch) + + id := append([]byte(alphabetUpdateIDPrefix), buf...) + var expFrostFSAlphabetUpd frostfscontract.AlphabetUpdatePrm + expFrostFSAlphabetUpd.SetID(id) + expFrostFSAlphabetUpd.SetPubs(testKeys.newAlphabetExp) + + require.EqualValues(t, []frostfscontract.AlphabetUpdatePrm{expFrostFSAlphabetUpd}, f.updates, "invalid FrostFS alphabet updates") +} + +type testKeys struct { + sidechainKeys keys.PublicKeys + mainnetKeys keys.PublicKeys + newAlphabetExp keys.PublicKeys + newInnerRingExp keys.PublicKeys +} + +func generateTestKeys(t *testing.T) testKeys { + for { + var result testKeys + + for i := 0; i < 4; i++ { + pk, err := keys.NewPrivateKey() + require.NoError(t, err, "failed to create private key") + result.sidechainKeys = append(result.sidechainKeys, pk.PublicKey()) + } + + result.mainnetKeys = append(result.mainnetKeys, result.sidechainKeys...) + pk, err := keys.NewPrivateKey() + require.NoError(t, err, "failed to create private key") + result.mainnetKeys = append(result.mainnetKeys, pk.PublicKey()) + + result.newAlphabetExp, err = newAlphabetList(result.sidechainKeys, result.mainnetKeys) + require.NoError(t, err, "failed to create expected new alphabet") + + if len(result.newAlphabetExp) == 0 { + continue //can be happen because of random and sort + } + + var irKeys keys.PublicKeys + irKeys = append(irKeys, result.sidechainKeys...) + result.newInnerRingExp, err = updateInnerRing(irKeys, result.sidechainKeys, result.newAlphabetExp) + require.NoError(t, err, "failed to create expected new IR") + sort.Sort(result.newInnerRingExp) + + return result + } +} + +type testEpochState struct { + epoch uint64 +} + +func (s *testEpochState) EpochCounter() uint64 { + return s.epoch +} + +type testAlphabetState struct { + isAlphabet bool +} + +func (s *testAlphabetState) IsAlphabet() bool { + return s.isAlphabet +} + +type testVoter struct { + votes []VoteValidatorPrm +} + +func (v *testVoter) VoteForSidechainValidator(prm VoteValidatorPrm) error { + v.votes = append(v.votes, prm) + return nil +} + +type testIRFetcher struct { + publicKeys keys.PublicKeys +} + +func (f *testIRFetcher) InnerRingKeys() (keys.PublicKeys, error) { + return f.publicKeys, nil +} + +type testMorphClient struct { + commiteeKeys keys.PublicKeys + + alphabetUpdates []client.UpdateAlphabetListPrm + notaryUpdates []client.UpdateNotaryListPrm +} + +func (c *testMorphClient) Committee() (res keys.PublicKeys, err error) { + return c.commiteeKeys, nil +} + +func (c *testMorphClient) UpdateNeoFSAlphabetList(prm client.UpdateAlphabetListPrm) error { + c.alphabetUpdates = append(c.alphabetUpdates, prm) + return nil +} + +func (c *testMorphClient) UpdateNotaryList(prm client.UpdateNotaryListPrm) error { + c.notaryUpdates = append(c.notaryUpdates, prm) + return nil +} + +type testMainnetClient struct { + alphabetKeys keys.PublicKeys + designateHash util.Uint160 +} + +func (c *testMainnetClient) NeoFSAlphabetList() (res keys.PublicKeys, err error) { + return c.alphabetKeys, nil +} + +func (c *testMainnetClient) GetDesignateHash() util.Uint160 { + return c.designateHash +} + +type testFrostFSClient struct { + updates []frostfscontract.AlphabetUpdatePrm +} + +func (c *testFrostFSClient) AlphabetUpdate(p frostfscontract.AlphabetUpdatePrm) error { + c.updates = append(c.updates, p) + return nil +} + +type testNetmapClient struct { + updates []nmClient.UpdateIRPrm +} + +func (c *testNetmapClient) UpdateInnerRing(p nmClient.UpdateIRPrm) error { + c.updates = append(c.updates, p) + return nil +} diff --git a/pkg/innerring/processors/governance/processor.go b/pkg/innerring/processors/governance/processor.go index b65dd17b7..e08bd3809 100644 --- a/pkg/innerring/processors/governance/processor.go +++ b/pkg/innerring/processors/governance/processor.go @@ -53,20 +53,39 @@ type ( InnerRingKeys() (keys.PublicKeys, error) } + FrostFSClient interface { + AlphabetUpdate(p frostfscontract.AlphabetUpdatePrm) error + } + + NetmapClient interface { + UpdateInnerRing(p nmClient.UpdateIRPrm) error + } + + MainnetClient interface { + NeoFSAlphabetList() (res keys.PublicKeys, err error) + GetDesignateHash() util.Uint160 + } + + MorphClient interface { + Committee() (res keys.PublicKeys, err error) + UpdateNeoFSAlphabetList(prm client.UpdateAlphabetListPrm) error + UpdateNotaryList(prm client.UpdateNotaryListPrm) error + } + // Processor of events related to governance in the network. Processor struct { log *logger.Logger pool *ants.Pool - frostfsClient *frostfscontract.Client - netmapClient *nmClient.Client + frostfsClient FrostFSClient + netmapClient NetmapClient alphabetState AlphabetState epochState EpochState voter Voter irFetcher IRFetcher - mainnetClient *client.Client - morphClient *client.Client + mainnetClient MainnetClient + morphClient MorphClient notaryDisabled bool @@ -82,10 +101,10 @@ type ( Voter Voter IRFetcher IRFetcher - MorphClient *client.Client - MainnetClient *client.Client - FrostFSClient *frostfscontract.Client - NetmapClient *nmClient.Client + MorphClient MorphClient + MainnetClient MainnetClient + FrostFSClient FrostFSClient + NetmapClient NetmapClient NotaryDisabled bool }