diff --git a/netmap/netmap_contract.go b/netmap/netmap_contract.go index a7e3bf6..621dbc9 100644 --- a/netmap/netmap_contract.go +++ b/netmap/netmap_contract.go @@ -190,8 +190,8 @@ func UpdateInnerRing(keys []interop.PublicKey) { common.SetSerialized(ctx, innerRingKey, irList) } -// Register method tries to add new candidate to the network map by -// emitting AddPeer notification. Should be invoked by the registree. +// Register method tries to add new candidate to the network map. +// Should be invoked in notary-enabled environment by the alphabet. func Register(nodeInfo []byte) { ctx := storage.GetContext() notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) @@ -281,13 +281,17 @@ func UpdateState(state int, publicKey interop.PublicKey) { if notaryDisabled { alphabet = common.AlphabetNodes() nodeKey = common.InnerRingInvoker(alphabet) - if len(nodeKey) == 0 { - common.CheckWitness(publicKey) + } - runtime.Notify("UpdateState", state, publicKey) - return - } + // If notary is enabled or caller is not an alphabet node, + // just emit the notification for alphabet. + if !notaryDisabled || len(nodeKey) == 0 { + common.CheckWitness(publicKey) + runtime.Notify("UpdateState", state, publicKey) + return + } + if notaryDisabled { threshold := len(alphabet)*2/3 + 1 id := common.InvokeID([]interface{}{state, publicKey}, []byte("update")) @@ -297,10 +301,6 @@ func UpdateState(state int, publicKey interop.PublicKey) { } common.RemoveVotes(ctx, id) - } else { - multiaddr := common.AlphabetAddress() - common.CheckWitness(publicKey) - common.CheckAlphabetWitness(multiaddr) } switch nodeState(state) { @@ -312,6 +312,25 @@ func UpdateState(state int, publicKey interop.PublicKey) { } } +// UpdateStateIR method tries to change node state in the network map. +// Should only be invoked in notary-enabled environment by the alphabet. +func UpdateStateIR(state nodeState, publicKey interop.PublicKey) { + ctx := storage.GetContext() + notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) + if notaryDisabled { + panic("UpdateStateIR should only be called in notary-enabled environment") + } + + common.CheckAlphabetWitness(common.AlphabetAddress()) + + switch state { + case offlineState: + removeFromNetmap(ctx, publicKey) + default: + panic("unsupported state") + } +} + // NewEpoch method changes epoch number up to provided epochNum argument. Can // be invoked only by Alphabet nodes. If provided epoch number is less or equal // current epoch number, method throws panic. diff --git a/tests/netmap_test.go b/tests/netmap_test.go index 8c6483c..797163f 100644 --- a/tests/netmap_test.go +++ b/tests/netmap_test.go @@ -111,25 +111,44 @@ func TestAddPeer(t *testing.T) { } func TestUpdateState(t *testing.T) { - e := newNetmapInvoker(t) + cNm := newNetmapInvoker(t) - acc := e.NewAccount(t) - cAcc := e.WithSigners(acc) - cBoth := e.WithSigners(e.Committee, acc) + acc := cNm.NewAccount(t) + cAcc := cNm.WithSigners(acc) dummyInfo := dummyNodeInfo(acc) - cBoth.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo.raw) + cAcc.Invoke(t, stackitem.Null{}, "addPeer", dummyInfo.raw) + cNm.Invoke(t, stackitem.Null{}, "register", dummyInfo.raw) pub, ok := vm.ParseSignatureContract(acc.Script()) require.True(t, ok) t.Run("missing witness", func(t *testing.T) { cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, - "updateState", int64(2), pub) - e.InvokeFail(t, common.ErrWitnessFailed, + "updateStateIR", int64(2), pub) + cNm.InvokeFail(t, common.ErrWitnessFailed, "updateState", int64(2), pub) }) - cBoth.Invoke(t, stackitem.Null{}, "updateState", int64(2), pub) + h := cAcc.Invoke(t, stackitem.Null{}, "updateState", int64(2), pub) + aer := cAcc.CheckHalt(t, h) + require.Equal(t, 1, len(aer.Events)) + require.Equal(t, "UpdateState", aer.Events[0].Name) + require.Equal(t, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(2)), + stackitem.NewByteArray(pub), + }), aer.Events[0].Item) + + // Check that updating happens only after `updateState` is called by the alphabet. + s, err := cAcc.TestInvoke(t, "netmapCandidates") + require.NoError(t, err) + require.Equal(t, 1, s.Len()) + + arr, ok := s.Pop().Value().([]stackitem.Item) + require.True(t, ok) + require.Equal(t, 1, len(arr)) + + cNm.Invoke(t, stackitem.Null{}, "updateStateIR", int64(2), pub) + cAcc.Invoke(t, stackitem.NewArray([]stackitem.Item{}), "netmapCandidates") }