code.vegaprotocol.io/vega@v0.79.0/core/validators/erc20multisig/topology_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 erc20multisig_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/core/validators"
    26  	"code.vegaprotocol.io/vega/core/validators/erc20multisig"
    27  	"code.vegaprotocol.io/vega/core/validators/erc20multisig/mocks"
    28  	"code.vegaprotocol.io/vega/logging"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/assert"
    32  )
    33  
    34  type testTopology struct {
    35  	*erc20multisig.Topology
    36  
    37  	ctrl           *gomock.Controller
    38  	broker         *bmocks.MockBroker
    39  	witness        *mocks.MockWitness
    40  	ocv            *mocks.MockMultiSigOnChainVerifier
    41  	ethEventSource *mocks.MockEthereumEventSource
    42  }
    43  
    44  func getTestTopology(t *testing.T) *testTopology {
    45  	t.Helper()
    46  	ctrl := gomock.NewController(t)
    47  	witness := mocks.NewMockWitness(ctrl)
    48  	ocv := mocks.NewMockMultiSigOnChainVerifier(ctrl)
    49  	broker := bmocks.NewMockBroker(ctrl)
    50  	ethEventSource := mocks.NewMockEthereumEventSource(ctrl)
    51  
    52  	top := &testTopology{
    53  		Topology: erc20multisig.NewTopology(
    54  			erc20multisig.NewDefaultConfig(),
    55  			logging.NewTestLogger(),
    56  			witness,
    57  			ocv,
    58  			broker,
    59  			"primary",
    60  		),
    61  		ctrl:           ctrl,
    62  		broker:         broker,
    63  		witness:        witness,
    64  		ocv:            ocv,
    65  		ethEventSource: ethEventSource,
    66  	}
    67  
    68  	top.SetEthereumEventSource(ethEventSource)
    69  	return top
    70  }
    71  
    72  func TestERC20Topology(t *testing.T) {
    73  	t.Run("valid threshold set event, threshold  updated", testValidThresholdEvent)
    74  	t.Run("invalid threshold set event, threshold not updated", testInvalidThresholdEvent)
    75  	t.Run("valid signer event, signers set updated", testValidSignerEvents)
    76  	t.Run("invalid signer event, signers set not updated", testInvalidSignerEvents)
    77  	t.Run("error on duplicate signer event", testErrorOnDuplicteSignerEvent)
    78  	t.Run("error on duplicate threshold set event", testErrorOnDuplicteThesholdSetEvent)
    79  }
    80  
    81  func testValidThresholdEvent(t *testing.T) {
    82  	top := getTestTopology(t)
    83  	defer top.ctrl.Finish()
    84  
    85  	top.OnTick(context.Background(), time.Unix(10, 0))
    86  
    87  	// first assert we have no signers
    88  	assert.Equal(t, top.GetThreshold(), uint32(0))
    89  
    90  	event := types.SignerThresholdSetEvent{
    91  		Threshold:   666,
    92  		BlockNumber: 10,
    93  		LogIndex:    11,
    94  		TxHash:      "0xacbde",
    95  		ID:          "someid",
    96  		Nonce:       "123",
    97  		BlockTime:   123456789,
    98  	}
    99  
   100  	var cb func(interface{}, bool)
   101  	var res validators.Resource
   102  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   103  		cb = f
   104  		res = r
   105  		return nil
   106  	})
   107  
   108  	assert.NoError(t, top.ProcessThresholdEvent(&event))
   109  
   110  	// now we can call the callback
   111  	cb(res, true)
   112  
   113  	// now we can update the time
   114  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   115  	top.OnTick(context.Background(), time.Unix(11, 0))
   116  	assert.Equal(t, top.GetThreshold(), uint32(666))
   117  
   118  	// now update it again at a later time
   119  	event2 := types.SignerThresholdSetEvent{
   120  		Threshold:   900,
   121  		BlockNumber: 11,
   122  		LogIndex:    7,
   123  		TxHash:      "0xedcba",
   124  		ID:          "someid2",
   125  		Nonce:       "321",
   126  		BlockTime:   123456790,
   127  	}
   128  
   129  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   130  		cb = f
   131  		res = r
   132  		return nil
   133  	})
   134  
   135  	assert.NoError(t, top.ProcessThresholdEvent(&event2))
   136  
   137  	// now we can call the callback
   138  	cb(res, true)
   139  
   140  	// now we can update the time
   141  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   142  	top.OnTick(context.Background(), time.Unix(11, 0))
   143  	assert.Equal(t, top.GetThreshold(), uint32(900))
   144  }
   145  
   146  func testInvalidThresholdEvent(t *testing.T) {
   147  	top := getTestTopology(t)
   148  	defer top.ctrl.Finish()
   149  
   150  	top.OnTick(context.Background(), time.Unix(10, 0))
   151  
   152  	// first assert we have no signers
   153  	assert.Equal(t, top.GetThreshold(), uint32(0))
   154  
   155  	event := types.SignerThresholdSetEvent{
   156  		Threshold:   666,
   157  		BlockNumber: 10,
   158  		LogIndex:    11,
   159  		TxHash:      "0xacbde",
   160  		ID:          "someid",
   161  		Nonce:       "123",
   162  		BlockTime:   123456789,
   163  	}
   164  
   165  	var cb func(interface{}, bool)
   166  	var res validators.Resource
   167  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   168  		cb = f
   169  		res = r
   170  		return nil
   171  	})
   172  
   173  	assert.NoError(t, top.ProcessThresholdEvent(&event))
   174  
   175  	// now we can call the callback
   176  	cb(res, false)
   177  
   178  	// now we can update the time
   179  	top.OnTick(context.Background(), time.Unix(11, 0))
   180  	assert.Equal(t, top.GetThreshold(), uint32(0))
   181  }
   182  
   183  func testInvalidSignerEvents(t *testing.T) {
   184  	top := getTestTopology(t)
   185  	defer top.ctrl.Finish()
   186  
   187  	top.OnTick(context.Background(), time.Unix(10, 0))
   188  
   189  	// first assert we have no signers
   190  	assert.Len(t, top.GetSigners(), 0)
   191  
   192  	event := types.SignerEvent{
   193  		BlockNumber: 10,
   194  		LogIndex:    11,
   195  		TxHash:      "0xacbde",
   196  		ID:          "someid",
   197  		Address:     "0x123456",
   198  		Nonce:       "123",
   199  		BlockTime:   123456789,
   200  		Kind:        types.SignerEventKindAdded,
   201  	}
   202  
   203  	var cb func(interface{}, bool)
   204  	var res validators.Resource
   205  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   206  		cb = f
   207  		res = r
   208  		return nil
   209  	})
   210  
   211  	assert.NoError(t, top.ProcessSignerEvent(&event))
   212  
   213  	// now we can call the callback
   214  	cb(res, false)
   215  
   216  	// now we can update the time
   217  	top.OnTick(context.Background(), time.Unix(11, 0))
   218  	assert.Len(t, top.GetSigners(), 0)
   219  }
   220  
   221  func testValidSignerEvents(t *testing.T) {
   222  	top := getTestTopology(t)
   223  	defer top.ctrl.Finish()
   224  
   225  	top.ocv.EXPECT().GetMultiSigAddress().AnyTimes()
   226  	top.ethEventSource.EXPECT().UpdateContractBlock(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   227  	top.OnTick(context.Background(), time.Unix(10, 0))
   228  
   229  	// first assert we have no signers
   230  	assert.Len(t, top.GetSigners(), 0)
   231  
   232  	event := types.SignerEvent{
   233  		BlockNumber: 10,
   234  		LogIndex:    11,
   235  		TxHash:      "0xacbde",
   236  		ID:          "someid",
   237  		Address:     "0x123456",
   238  		Nonce:       "123",
   239  		BlockTime:   123456789,
   240  		Kind:        types.SignerEventKindAdded,
   241  	}
   242  
   243  	var cb func(interface{}, bool)
   244  	var res validators.Resource
   245  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   246  		cb = f
   247  		res = r
   248  		return nil
   249  	})
   250  
   251  	assert.NoError(t, top.ProcessSignerEvent(&event))
   252  
   253  	// now we can call the callback
   254  	cb(res, true)
   255  
   256  	// now we can update the time
   257  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   258  	top.OnTick(context.Background(), time.Unix(11, 0))
   259  
   260  	t.Run("ensure the signer list is updated", func(t *testing.T) {
   261  		signers := top.GetSigners()
   262  		assert.Len(t, signers, 1)
   263  		assert.Equal(t, "0x123456", signers[0])
   264  	})
   265  
   266  	t.Run("check if our party IsSigner", func(t *testing.T) {
   267  		assert.True(t, top.IsSigner("0x123456"))
   268  	})
   269  
   270  	t.Run("check excess signers", func(t *testing.T) {
   271  		okAddresses := []string{"0x123456"}
   272  		koAddresses := []string{}
   273  
   274  		assert.True(t, top.ExcessSigners(koAddresses))
   275  		assert.False(t, top.ExcessSigners(okAddresses))
   276  	})
   277  
   278  	// now we try to delete it yeay!
   279  	event2 := types.SignerEvent{
   280  		BlockNumber: 11,
   281  		LogIndex:    4,
   282  		TxHash:      "0xedcba",
   283  		ID:          "someid2",
   284  		Address:     "0x123456",
   285  		Nonce:       "321",
   286  		BlockTime:   123456790,
   287  		Kind:        types.SignerEventKindRemoved,
   288  	}
   289  
   290  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(func(r validators.Resource, f func(interface{}, bool), _ time.Time) error {
   291  		cb = f
   292  		res = r
   293  		return nil
   294  	})
   295  
   296  	assert.NoError(t, top.ProcessSignerEvent(&event2))
   297  
   298  	// now we can call the callback again!
   299  	cb(res, true)
   300  
   301  	// now we can update the time
   302  	top.broker.EXPECT().Send(gomock.Any()).Times(1)
   303  	top.OnTick(context.Background(), time.Unix(12, 0))
   304  
   305  	t.Run("ensure all signers have been removed", func(t *testing.T) {
   306  		signers := top.GetSigners()
   307  		assert.Len(t, signers, 0)
   308  	})
   309  }
   310  
   311  func testErrorOnDuplicteSignerEvent(t *testing.T) {
   312  	top := getTestTopology(t)
   313  	defer top.ctrl.Finish()
   314  
   315  	event := types.SignerEvent{
   316  		BlockNumber: 10,
   317  		LogIndex:    11,
   318  		TxHash:      "0xacbde",
   319  		ID:          "someid",
   320  		Address:     "0x123456",
   321  		Nonce:       "123",
   322  		BlockTime:   123456789,
   323  		Kind:        types.SignerEventKindAdded,
   324  	}
   325  
   326  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil)
   327  	assert.NoError(t, top.ProcessSignerEvent(&event))
   328  
   329  	assert.EqualError(t,
   330  		top.ProcessSignerEvent(&event),
   331  		erc20multisig.ErrDuplicatedSignerEvent.Error(),
   332  	)
   333  }
   334  
   335  func testErrorOnDuplicteThesholdSetEvent(t *testing.T) {
   336  	top := getTestTopology(t)
   337  	defer top.ctrl.Finish()
   338  
   339  	event := types.SignerThresholdSetEvent{
   340  		BlockNumber: 10,
   341  		LogIndex:    11,
   342  		TxHash:      "0xacbde",
   343  		ID:          "someid",
   344  		Threshold:   666,
   345  		Nonce:       "123",
   346  		BlockTime:   123456789,
   347  	}
   348  
   349  	top.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil)
   350  	assert.NoError(t, top.ProcessThresholdEvent(&event))
   351  
   352  	assert.EqualError(t,
   353  		top.ProcessThresholdEvent(&event),
   354  		erc20multisig.ErrDuplicatedThresholdEvent.Error(),
   355  	)
   356  }