code.vegaprotocol.io/vega@v0.79.0/core/staking/stake_verifier_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 staking_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/staking"
    25  	"code.vegaprotocol.io/vega/core/staking/mocks"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/core/validators"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/logging"
    30  
    31  	"github.com/golang/mock/gomock"
    32  	"github.com/stretchr/testify/assert"
    33  )
    34  
    35  type stakeVerifierTest struct {
    36  	*staking.StakeVerifier
    37  
    38  	ctrl    *gomock.Controller
    39  	tsvc    *mocks.MockTimeService
    40  	broker  *bmocks.MockBroker
    41  	accs    *staking.Accounting
    42  	ocv     *mocks.MockEthOnChainVerifier
    43  	witness *mocks.MockWitness
    44  	evtfwd  *mocks.MockEvtForwarder
    45  	evtSrc  *mocks.MockEthereumEventSource
    46  
    47  	onTick func(context.Context, time.Time)
    48  }
    49  
    50  func getStakeVerifierTest(t *testing.T) *stakeVerifierTest {
    51  	t.Helper()
    52  	ctrl := gomock.NewController(t)
    53  	broker := bmocks.NewMockBroker(ctrl)
    54  	log := logging.NewTestLogger()
    55  	cfg := staking.NewDefaultConfig()
    56  	ocv := mocks.NewMockEthOnChainVerifier(ctrl)
    57  	ts := mocks.NewMockTimeService(ctrl)
    58  	witness := mocks.NewMockWitness(ctrl)
    59  	evtfwd := mocks.NewMockEvtForwarder(ctrl)
    60  	evtSrc := mocks.NewMockEthereumEventSource(ctrl)
    61  
    62  	accs := staking.NewAccounting(log, cfg, ts, broker, nil, evtfwd, witness, true, evtSrc)
    63  
    64  	svt := &stakeVerifierTest{
    65  		StakeVerifier: staking.NewStakeVerifier(log, cfg, accs, witness, ts, broker, ocv, evtSrc),
    66  		ctrl:          ctrl,
    67  		broker:        broker,
    68  		accs:          accs,
    69  		ocv:           ocv,
    70  		tsvc:          ts,
    71  		witness:       witness,
    72  		evtfwd:        evtfwd,
    73  		evtSrc:        evtSrc,
    74  	}
    75  	svt.onTick = svt.StakeVerifier.OnTick
    76  
    77  	return svt
    78  }
    79  
    80  func TestStakeVerifier(t *testing.T) {
    81  	t.Run("can process stake event deposited OK", testProcessStakeEventDepositedOK)
    82  	t.Run("can process stake event deposited KO", testProcessStakeEventDepositedKO)
    83  	t.Run("can process stake event removed OK", testProcessStakeEventRemovedOK)
    84  	t.Run("can process stake event removed KO", testProcessStakeEventRemovedKO)
    85  	t.Run("can process multiple events OK", testProcessStakeEventMultiOK)
    86  	t.Run("duplicates", testDuplicates)
    87  }
    88  
    89  func testProcessStakeEventDepositedOK(t *testing.T) {
    90  	stakev := getStakeVerifierTest(t)
    91  	defer stakev.ctrl.Finish()
    92  	assert.NotNil(t, stakev)
    93  
    94  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
    95  	stakev.broker.EXPECT().Send(gomock.Any()).Times(2)
    96  
    97  	var f func(interface{}, bool)
    98  	var evt interface{}
    99  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   100  		Times(1).
   101  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   102  			f = fn
   103  			evt = evtR
   104  			return nil
   105  		})
   106  
   107  	event := &types.StakeDeposited{
   108  		BlockNumber:     42,
   109  		LogIndex:        1789,
   110  		TxID:            "somehash",
   111  		ID:              "someid",
   112  		VegaPubKey:      "somepubkey",
   113  		EthereumAddress: "0xnothex",
   114  		Amount:          num.NewUint(1000),
   115  		BlockTime:       100000,
   116  	}
   117  
   118  	err := stakev.ProcessStakeDeposited(context.Background(), event)
   119  
   120  	assert.NoError(t, err)
   121  	assert.NotNil(t, f)
   122  
   123  	// now we'll use the callback to set the event OK
   124  	// no expectation there.
   125  	f(evt, true)
   126  
   127  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   128  	stakev.ocv.EXPECT().GetStakingBridgeAddresses().Times(1)
   129  	stakev.onTick(context.Background(), time.Unix(10, 0))
   130  
   131  	balance, err := stakev.accs.GetAvailableBalance("somepubkey")
   132  	assert.NoError(t, err)
   133  	assert.Equal(t, 1000, int(balance.Uint64()))
   134  }
   135  
   136  func testProcessStakeEventDepositedKO(t *testing.T) {
   137  	stakev := getStakeVerifierTest(t)
   138  	defer stakev.ctrl.Finish()
   139  	assert.NotNil(t, stakev)
   140  
   141  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
   142  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   143  
   144  	var f func(interface{}, bool)
   145  	var evt interface{}
   146  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   147  		Times(1).
   148  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   149  			f = fn
   150  			evt = evtR
   151  			return nil
   152  		})
   153  
   154  	event := &types.StakeDeposited{
   155  		BlockNumber:     42,
   156  		LogIndex:        1789,
   157  		TxID:            "somehash",
   158  		ID:              "someid",
   159  		VegaPubKey:      "somepubkey",
   160  		EthereumAddress: "0xnothex",
   161  		Amount:          num.NewUint(1000),
   162  		BlockTime:       100000,
   163  	}
   164  
   165  	err := stakev.ProcessStakeDeposited(context.Background(), event)
   166  
   167  	assert.NoError(t, err)
   168  	assert.NotNil(t, f)
   169  
   170  	// now we'll use the callback to set the event OK
   171  	// no expectation there.
   172  	f(evt, false)
   173  
   174  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   175  	stakev.onTick(context.Background(), time.Unix(10, 0))
   176  
   177  	balance, err := stakev.accs.GetAvailableBalance("somepubkey")
   178  	assert.EqualError(t, err, staking.ErrNoBalanceForParty.Error())
   179  	assert.Equal(t, 0, int(balance.Uint64()))
   180  }
   181  
   182  func testProcessStakeEventRemovedOK(t *testing.T) {
   183  	stakev := getStakeVerifierTest(t)
   184  	defer stakev.ctrl.Finish()
   185  	assert.NotNil(t, stakev)
   186  
   187  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
   188  	stakev.broker.EXPECT().Send(gomock.Any()).Times(2)
   189  
   190  	var f func(interface{}, bool)
   191  	var evt interface{}
   192  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   193  		Times(1).
   194  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   195  			f = fn
   196  			evt = evtR
   197  			return nil
   198  		})
   199  
   200  	event := &types.StakeRemoved{
   201  		BlockNumber:     42,
   202  		LogIndex:        1789,
   203  		TxID:            "somehash",
   204  		ID:              "someid",
   205  		VegaPubKey:      "somepubkey",
   206  		EthereumAddress: "0xnothex",
   207  		Amount:          num.NewUint(1000),
   208  		BlockTime:       100000,
   209  	}
   210  
   211  	err := stakev.ProcessStakeRemoved(context.Background(), event)
   212  
   213  	assert.NoError(t, err)
   214  	assert.NotNil(t, f)
   215  
   216  	// now we'll use the callback to set the event OK
   217  	// no expectation there.
   218  	f(evt, true)
   219  
   220  	stakev.ocv.EXPECT().GetStakingBridgeAddresses().Times(1)
   221  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   222  	stakev.onTick(context.Background(), time.Unix(10, 0))
   223  
   224  	// we get a 0 balance, as the only event is a removed.
   225  	balance, err := stakev.accs.GetAvailableBalance("somepubkey")
   226  	assert.NoError(t, err)
   227  	assert.Equal(t, 0, int(balance.Uint64()))
   228  }
   229  
   230  func testProcessStakeEventRemovedKO(t *testing.T) {
   231  	stakev := getStakeVerifierTest(t)
   232  	defer stakev.ctrl.Finish()
   233  	assert.NotNil(t, stakev)
   234  
   235  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
   236  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   237  
   238  	var f func(interface{}, bool)
   239  	var evt interface{}
   240  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   241  		Times(1).
   242  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   243  			f = fn
   244  			evt = evtR
   245  			return nil
   246  		})
   247  
   248  	event := &types.StakeRemoved{
   249  		BlockNumber:     42,
   250  		LogIndex:        1789,
   251  		TxID:            "somehash",
   252  		ID:              "someid",
   253  		VegaPubKey:      "somepubkey",
   254  		EthereumAddress: "0xnothex",
   255  		Amount:          num.NewUint(1000),
   256  		BlockTime:       100000,
   257  	}
   258  
   259  	err := stakev.ProcessStakeRemoved(context.Background(), event)
   260  
   261  	assert.NoError(t, err)
   262  	assert.NotNil(t, f)
   263  
   264  	// now we'll use the callback to set the event OK
   265  	// no expectation there.
   266  	f(evt, false)
   267  
   268  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   269  	stakev.onTick(context.Background(), time.Unix(10, 0))
   270  
   271  	balance, err := stakev.accs.GetAvailableBalance("somepubkey")
   272  	assert.EqualError(t, err, staking.ErrNoBalanceForParty.Error())
   273  	assert.Equal(t, 0, int(balance.Uint64()))
   274  }
   275  
   276  func testProcessStakeEventMultiOK(t *testing.T) {
   277  	stakev := getStakeVerifierTest(t)
   278  	defer stakev.ctrl.Finish()
   279  	assert.NotNil(t, stakev)
   280  
   281  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
   282  	stakev.broker.EXPECT().Send(gomock.Any()).Times(2)
   283  	stakev.ocv.EXPECT().GetStakingBridgeAddresses().AnyTimes()
   284  
   285  	var f func(interface{}, bool)
   286  	var evt interface{}
   287  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   288  		Times(1).
   289  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   290  			f = fn
   291  			evt = evtR
   292  			return nil
   293  		})
   294  
   295  	event := &types.StakeDeposited{
   296  		BlockNumber:     42,
   297  		LogIndex:        1789,
   298  		TxID:            "somehash",
   299  		ID:              "someid",
   300  		VegaPubKey:      "somepubkey",
   301  		EthereumAddress: "0xnothex",
   302  		Amount:          num.NewUint(1000),
   303  		BlockTime:       100000,
   304  	}
   305  
   306  	err := stakev.ProcessStakeDeposited(context.Background(), event)
   307  
   308  	assert.NoError(t, err)
   309  	assert.NotNil(t, f)
   310  
   311  	// now we'll use the callback to set the event OK
   312  	// no expectation there.
   313  	f(evt, true)
   314  
   315  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   316  	stakev.onTick(context.Background(), time.Unix(10, 0))
   317  
   318  	balance, err := stakev.accs.GetAvailableBalance("somepubkey")
   319  	assert.NoError(t, err)
   320  	assert.Equal(t, 1000, int(balance.Uint64()))
   321  
   322  	// no we remove some stake
   323  
   324  	stakev.tsvc.EXPECT().GetTimeNow().Times(2)
   325  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   326  	f = nil
   327  	evt = nil
   328  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   329  		Times(1).
   330  		DoAndReturn(func(evtR validators.Resource, fn func(interface{}, bool), _ time.Time) error {
   331  			f = fn
   332  			evt = evtR
   333  			return nil
   334  		})
   335  
   336  	eventR := &types.StakeRemoved{
   337  		BlockNumber:     42,
   338  		LogIndex:        1789,
   339  		TxID:            "somehash",
   340  		ID:              "someid2",
   341  		VegaPubKey:      "somepubkey",
   342  		EthereumAddress: "0xnothex",
   343  		Amount:          num.NewUint(500),
   344  		BlockTime:       200000,
   345  	}
   346  
   347  	err = stakev.ProcessStakeRemoved(context.Background(), eventR)
   348  
   349  	assert.NoError(t, err)
   350  	assert.NotNil(t, f)
   351  
   352  	// now we'll use the callback to set the event OK
   353  	// no expectation there.
   354  	f(evt, true)
   355  
   356  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   357  	stakev.onTick(context.Background(), time.Unix(10, 0))
   358  
   359  	balance, err = stakev.accs.GetAvailableBalance("somepubkey")
   360  	assert.NoError(t, err)
   361  	assert.Equal(t, 500, int(balance.Uint64()))
   362  }
   363  
   364  func testDuplicates(t *testing.T) {
   365  	stakev := getStakeVerifierTest(t)
   366  	defer stakev.ctrl.Finish()
   367  	assert.NotNil(t, stakev)
   368  
   369  	stakev.tsvc.EXPECT().GetTimeNow().Times(1)
   370  	stakev.broker.EXPECT().Send(gomock.Any()).Times(1)
   371  
   372  	stakev.witness.EXPECT().StartCheck(gomock.Any(), gomock.Any(), gomock.Any()).
   373  		AnyTimes()
   374  	event := &types.StakeDeposited{
   375  		BlockNumber:     42,
   376  		LogIndex:        1789,
   377  		TxID:            "somehash",
   378  		ID:              "someid",
   379  		VegaPubKey:      "somepubkey",
   380  		EthereumAddress: "0xnothex",
   381  		Amount:          num.NewUint(1000),
   382  		BlockTime:       100000,
   383  	}
   384  
   385  	// no error at first
   386  	err := stakev.ProcessStakeDeposited(context.Background(), event)
   387  	assert.NoError(t, err)
   388  	// same event
   389  	err = stakev.ProcessStakeDeposited(context.Background(), event)
   390  	assert.EqualError(t, err, staking.ErrDuplicatedStakeDepositedEvent.Error())
   391  
   392  	event2 := &types.StakeRemoved{
   393  		BlockNumber:     42,
   394  		LogIndex:        1789,
   395  		TxID:            "somehash",
   396  		ID:              "someid",
   397  		VegaPubKey:      "somepubkey",
   398  		EthereumAddress: "0xnothex",
   399  		Amount:          num.NewUint(1000),
   400  		BlockTime:       100000,
   401  	}
   402  	// stake removed now
   403  	err = stakev.ProcessStakeRemoved(context.Background(), event2)
   404  	assert.EqualError(t, err, staking.ErrDuplicatedStakeRemovedEvent.Error())
   405  }