code.vegaprotocol.io/vega@v0.79.0/core/validators/signatures_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package validators_test 17 18 import ( 19 "context" 20 "crypto/ed25519" 21 "encoding/hex" 22 "testing" 23 "time" 24 25 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 26 "code.vegaprotocol.io/vega/core/events" 27 "code.vegaprotocol.io/vega/core/types" 28 "code.vegaprotocol.io/vega/core/validators" 29 "code.vegaprotocol.io/vega/core/validators/mocks" 30 "code.vegaprotocol.io/vega/logging" 31 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/golang/mock/gomock" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 ) 37 38 var chainID = "12" 39 40 type testSignatures struct { 41 *validators.ERC20Signatures 42 notary *mocks.MockNotary 43 ctrl *gomock.Controller 44 broker *bmocks.MockBroker 45 signer testSigner 46 multisigTopology *mocks.MockMultiSigTopology 47 secondaryMultisigTopology *mocks.MockMultiSigTopology 48 } 49 50 func getTestSignatures(t *testing.T) *testSignatures { 51 t.Helper() 52 ctrl := gomock.NewController(t) 53 notary := mocks.NewMockNotary(ctrl) 54 broker := bmocks.NewMockBroker(ctrl) 55 nodewallet := mocks.NewMockNodeWallets(ctrl) 56 multisigTopology := mocks.NewMockMultiSigTopology(ctrl) 57 secondaryMultisigTopology := mocks.NewMockMultiSigTopology(ctrl) 58 tsigner := testSigner{} 59 nodewallet.EXPECT().GetEthereum().AnyTimes().Return(tsigner) 60 61 return &testSignatures{ 62 ERC20Signatures: validators.NewSignatures( 63 logging.NewTestLogger(), 64 multisigTopology, 65 secondaryMultisigTopology, 66 notary, 67 nodewallet, 68 broker, 69 true, 70 ), 71 ctrl: ctrl, 72 notary: notary, 73 broker: broker, 74 signer: tsigner, 75 multisigTopology: multisigTopology, 76 secondaryMultisigTopology: secondaryMultisigTopology, 77 } 78 } 79 80 func TestPromotionSignatures(t *testing.T) { 81 ctx := context.Background() 82 signatures := getTestSignatures(t) 83 defer signatures.ctrl.Finish() 84 85 // previous state, 2 validators, 1 non validator 86 previousState := map[string]validators.StatusAddress{ 87 "8fd85dac403623ea3b894e9e342571716eedf550b3b1953e2c29eb58a6da683a": { 88 Status: validators.ValidatorStatusTendermint, 89 EthAddress: "0xddDFA1974b156336b9c49579A2bC4e0a7059CAD0", 90 }, 91 "927cbf8d5909cc017cf78ea9806fd57c3115d37e481eaf9d866f526b356f3ced": { 92 Status: validators.ValidatorStatusTendermint, 93 EthAddress: "0x5945ae02D5EE15181cc4AC0f5EaeF4C25Dc17Aa8", 94 }, 95 "95893347980299679883f817f118718f949826d1a0a1c2e4f22ba5f0cd6d1f5d": { 96 Status: validators.ValidatorStatusTendermint, 97 EthAddress: "0x539ac90d9523f878779491D4175dc11AD09972F0", 98 }, 99 "4554375ce61b6828c6f7b625b7735034496b7ea19951509cccf4eb2ba35011b0": { 100 Status: validators.ValidatorStatusErsatz, 101 EthAddress: "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90", 102 }, 103 } 104 // based on the previous state, the validators in order: 105 // - 1 stays a validator 106 // - 1 validators became erzatz 107 // - 1 validators completely removed 108 // - 1 erzatz became validator 109 110 newState := map[string]validators.StatusAddress{ 111 "8fd85dac403623ea3b894e9e342571716eedf550b3b1953e2c29eb58a6da683a": { 112 Status: validators.ValidatorStatusTendermint, 113 EthAddress: "0xddDFA1974b156336b9c49579A2bC4e0a7059CAD0", 114 }, 115 "927cbf8d5909cc017cf78ea9806fd57c3115d37e481eaf9d866f526b356f3ced": { 116 Status: validators.ValidatorStatusErsatz, 117 EthAddress: "0x5945ae02D5EE15181cc4AC0f5EaeF4C25Dc17Aa8", 118 }, 119 "4554375ce61b6828c6f7b625b7735034496b7ea19951509cccf4eb2ba35011b0": { 120 Status: validators.ValidatorStatusTendermint, 121 EthAddress: "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90", 122 }, 123 } 124 125 currentTime := time.Unix(10, 0) 126 127 // just aggregate all events 128 // we'll verify their content after 129 evts := []events.Event{} 130 signatures.broker.EXPECT().SendBatch(gomock.Any()).Times(5).DoAndReturn(func(newEvts []events.Event) { 131 evts = append(evts, newEvts...) 132 }) 133 134 signatures.notary.EXPECT().StartAggregate(gomock.Any(), gomock.Any(), gomock.Any()).Times(5) 135 signatures.multisigTopology.EXPECT().ChainID().Times(5).Return(chainID) 136 // now, there's no assertion to do just now, this only send a sh*t ton of events 137 signatures.PreparePromotionsSignatures( 138 ctx, 139 currentTime, 140 12, 141 previousState, 142 newState, 143 ) 144 145 assert.Len(t, evts, 0) 146 147 // now request the signature bundle for the adding 148 err := signatures.EmitValidatorAddedSignatures(ctx, "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90", "4554375ce61b6828c6f7b625b7735034496b7ea19951509cccf4eb2ba35011b0", chainID, currentTime) 149 require.NoError(t, err) 150 151 // now ask for all the removes, each tendermint validator will ask 152 toAsk := []string{ 153 "0xddDFA1974b156336b9c49579A2bC4e0a7059CAD0", 154 "0x5945ae02D5EE15181cc4AC0f5EaeF4C25Dc17Aa8", 155 } 156 157 for _, v := range toAsk { 158 err = signatures.EmitValidatorRemovedSignatures(ctx, v, "927cbf8d5909cc017cf78ea9806fd57c3115d37e481eaf9d866f526b356f3ced", chainID, currentTime) 159 require.NoError(t, err) 160 } 161 162 for _, v := range toAsk { 163 err = signatures.EmitValidatorRemovedSignatures(ctx, v, "95893347980299679883f817f118718f949826d1a0a1c2e4f22ba5f0cd6d1f5d", chainID, currentTime) 164 require.NoError(t, err) 165 } 166 167 require.Len(t, evts, 5) 168 169 t.Run("ensure all correct events are sent", func(t *testing.T) { 170 add1, ok := evts[0].(*events.ERC20MultiSigSignerAdded) 171 assert.True(t, ok, "invalid event, expected SignedAdded") 172 assert.Equal(t, add1.ERC20MultiSigSignerAdded().NewSigner, "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90") 173 174 remove1, ok := evts[1].(*events.ERC20MultiSigSignerRemoved) 175 assert.True(t, ok, "invalid event, expected SignedRemoved") 176 assert.Equal(t, remove1.ERC20MultiSigSignerRemoved().OldSigner, "0x5945ae02D5EE15181cc4AC0f5EaeF4C25Dc17Aa8") 177 178 remove2, ok := evts[1].(*events.ERC20MultiSigSignerRemoved) 179 assert.True(t, ok, "invalid event, expected SignedRemoved") 180 assert.Equal(t, remove2.ERC20MultiSigSignerRemoved().OldSigner, "0x5945ae02D5EE15181cc4AC0f5EaeF4C25Dc17Aa8") 181 182 // check the two removes on the same node have the same nonce 183 assert.Equal(t, remove1.ERC20MultiSigSignerRemoved().Nonce, remove2.ERC20MultiSigSignerRemoved().Nonce) 184 185 remove3, ok := evts[3].(*events.ERC20MultiSigSignerRemoved) 186 assert.True(t, ok, "invalid event, expected SignedRemoved") 187 assert.Equal(t, remove3.ERC20MultiSigSignerRemoved().OldSigner, "0x539ac90d9523f878779491D4175dc11AD09972F0") 188 189 remove4, ok := evts[4].(*events.ERC20MultiSigSignerRemoved) 190 assert.True(t, ok, "invalid event, expected SignedRemoved") 191 assert.Equal(t, remove4.ERC20MultiSigSignerRemoved().OldSigner, "0x539ac90d9523f878779491D4175dc11AD09972F0") 192 193 assert.Equal(t, remove3.ERC20MultiSigSignerRemoved().Nonce, remove4.ERC20MultiSigSignerRemoved().Nonce) 194 }) 195 196 t.Run("test snapshots", func(t *testing.T) { 197 state := signatures.SerialisePendingSignatures() 198 snap := getTestSignatures(t) 199 snap.RestorePendingSignatures(state) 200 201 snap.broker.EXPECT().SendBatch(gomock.Any()).Times(len(toAsk) + 1) 202 snap.multisigTopology.EXPECT().ChainID().Times(len(toAsk) + 1).Return(chainID) 203 204 // check the pending signatures still exist (we get no error) and that "already issued" is restored (notary mock should not expect anything) 205 require.NoError(t, snap.EmitValidatorAddedSignatures(ctx, "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90", "4554375ce61b6828c6f7b625b7735034496b7ea19951509cccf4eb2ba35011b0", chainID, currentTime)) 206 for _, v := range toAsk { 207 require.NoError(t, snap.EmitValidatorRemovedSignatures(ctx, v, "95893347980299679883f817f118718f949826d1a0a1c2e4f22ba5f0cd6d1f5d", chainID, currentTime)) 208 } 209 }) 210 211 t.Run("clear stale remove signatures", func(t *testing.T) { 212 // return that the signers are not on the contract 213 signatures.multisigTopology.EXPECT().IsSigner(gomock.Any()).Return(false).Times(3) 214 signatures.secondaryMultisigTopology.EXPECT().IsSigner(gomock.Any()).Return(false).Times(2) 215 signatures.ClearStaleSignatures() 216 217 // we should get no signatures for the removed nodes 218 require.Error(t, validators.ErrNoPendingSignaturesForNodeID, signatures.EmitValidatorRemovedSignatures(ctx, "submitter", "927cbf8d5909cc017cf78ea9806fd57c3115d37e481eaf9d866f526b356f3ced", chainID, currentTime)) 219 220 // now for the add signatures 221 signatures.multisigTopology.EXPECT().IsSigner(gomock.Any()).Return(true).Times(1) 222 signatures.secondaryMultisigTopology.EXPECT().IsSigner(gomock.Any()).Return(true).Times(1) 223 signatures.ClearStaleSignatures() 224 require.Error(t, validators.ErrNoPendingSignaturesForNodeID, signatures.EmitValidatorAddedSignatures(ctx, "0x7629Faf5B7a3BB167B6f2F86DB5fB7f13B20Ee90", "4554375ce61b6828c6f7b625b7735034496b7ea19951509cccf4eb2ba35011b0", chainID, currentTime)) 225 }) 226 } 227 228 func TestOfferSignatures(t *testing.T) { 229 ctx := context.Background() 230 signatures := getTestSignatures(t) 231 defer signatures.ctrl.Finish() 232 233 signatures.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 234 235 var addSigResID string 236 var addSig []byte 237 signatures.notary.EXPECT(). 238 StartAggregate(gomock.Any(), types.NodeSignatureKindERC20MultiSigSignerAdded, gomock.Any()).DoAndReturn( 239 func(resID string, kind types.NodeSignatureKind, signature []byte) { 240 addSigResID = resID 241 addSig = signature 242 }, 243 ) 244 245 var removeSigResID string 246 var removeSig []byte 247 signatures.notary.EXPECT(). 248 StartAggregate(gomock.Any(), types.NodeSignatureKindERC20MultiSigSignerRemoved, gomock.Any()).DoAndReturn( 249 func(resID string, kind types.NodeSignatureKind, signature []byte) { 250 removeSigResID = resID 251 removeSig = signature 252 }, 253 ) 254 signatures.multisigTopology.EXPECT().ChainID().Times(1).Return(chainID) 255 256 submitter := "node_operator" 257 now := time.Now() 258 validator := validators.NodeIDAddress{NodeID: "node_1", EthAddress: "eth_address"} 259 260 signatures.PrepareValidatorSignatures(ctx, []validators.NodeIDAddress{validator}, 1, false) 261 err := signatures.EmitValidatorRemovedSignatures(ctx, submitter, validator.NodeID, chainID, now) 262 require.NoError(t, err) 263 264 signatures.multisigTopology.EXPECT().ChainID().Times(1).Return(chainID) 265 validator.EthAddress = "updated_eth_address" 266 267 signatures.PrepareValidatorSignatures(ctx, []validators.NodeIDAddress{validator}, 1, true) 268 err = signatures.EmitValidatorAddedSignatures(ctx, submitter, validator.NodeID, chainID, now) 269 require.NoError(t, err) 270 271 signatures.notary.EXPECT(). 272 OfferSignatures(types.NodeSignatureKindERC20MultiSigSignerAdded, gomock.Any()).DoAndReturn( 273 func(kind types.NodeSignatureKind, f func(id string) []byte) { 274 require.Equal(t, addSig, f(addSigResID)) 275 }, 276 ) 277 278 signatures.notary.EXPECT(). 279 OfferSignatures(types.NodeSignatureKindERC20MultiSigSignerRemoved, gomock.Any()).DoAndReturn( 280 func(kind types.NodeSignatureKind, f func(id string) []byte) { 281 require.Equal(t, removeSig, f(removeSigResID)) 282 }, 283 ) 284 signatures.multisigTopology.EXPECT().ChainID().Times(2).Return(chainID) 285 signatures.OfferSignatures() 286 } 287 288 const ( 289 privKey = "9feb9cbee69c1eeb30db084544ff8bf92166bf3fddefa6a021b458b4de04c66758a127387b1dff15b71fd7d0a9fd104ed75da4aac549efd5d149051ea57cefaf" 290 pubKey = "58a127387b1dff15b71fd7d0a9fd104ed75da4aac549efd5d149051ea57cefaf" 291 ) 292 293 type testSigner struct{} 294 295 func (s testSigner) Algo() string { return "ed25519" } 296 297 func (s testSigner) Sign(msg []byte) ([]byte, error) { 298 priv, _ := hex.DecodeString(privKey) 299 300 return ed25519.Sign(ed25519.PrivateKey(priv), msg), nil 301 } 302 303 func (s testSigner) Verify(msg, sig []byte) bool { 304 pub, _ := hex.DecodeString(pubKey) 305 hash := crypto.Keccak256(msg) 306 307 return ed25519.Verify(ed25519.PublicKey(pub), hash, sig) 308 }