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  }