code.vegaprotocol.io/vega@v0.79.0/core/validators/witness_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  	"encoding/hex"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/txn"
    26  	"code.vegaprotocol.io/vega/core/validators"
    27  	"code.vegaprotocol.io/vega/core/validators/mocks"
    28  	"code.vegaprotocol.io/vega/libs/crypto"
    29  	"code.vegaprotocol.io/vega/libs/num"
    30  	"code.vegaprotocol.io/vega/logging"
    31  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    32  
    33  	"github.com/cenkalti/backoff"
    34  	"github.com/golang/mock/gomock"
    35  	"github.com/golang/protobuf/proto"
    36  	"github.com/stretchr/testify/assert"
    37  	"github.com/stretchr/testify/require"
    38  )
    39  
    40  type testWitness struct {
    41  	*validators.Witness
    42  	ctrl      *gomock.Controller
    43  	top       *mocks.MockValidatorTopology
    44  	cmd       *mocks.MockCommander
    45  	tsvc      *mocks.MockTimeService
    46  	startTime time.Time
    47  }
    48  
    49  func getTestWitness(t *testing.T) *testWitness {
    50  	t.Helper()
    51  	ctrl := gomock.NewController(t)
    52  	top := mocks.NewMockValidatorTopology(ctrl)
    53  	cmd := mocks.NewMockCommander(ctrl)
    54  	tsvc := mocks.NewMockTimeService(ctrl)
    55  
    56  	now := time.Now()
    57  	tsvc.EXPECT().GetTimeNow().Times(1).Return(now)
    58  	w := validators.NewWitness(context.Background(),
    59  		logging.NewTestLogger(), validators.NewDefaultConfig(), top, cmd, tsvc)
    60  	assert.NotNil(t, w)
    61  
    62  	return &testWitness{
    63  		Witness:   w,
    64  		ctrl:      ctrl,
    65  		top:       top,
    66  		cmd:       cmd,
    67  		tsvc:      tsvc,
    68  		startTime: now,
    69  	}
    70  }
    71  
    72  func TestExtResCheck(t *testing.T) {
    73  	t.Run("start - error duplicate", testStartErrorDuplicate)
    74  	t.Run("start - error check failed", testStartErrorCheckFailed)
    75  	t.Run("start - OK", testStartOK)
    76  	t.Run("add node vote - error invalid id", testNodeVoteInvalidProposalReference)
    77  	t.Run("add node vote - error note a validator", testNodeVoteNotAValidator)
    78  	t.Run("add node vote - error duplicate vote", testNodeVoteDuplicateVote)
    79  	t.Run("add node vote - OK", testNodeVoteOK)
    80  	t.Run("on chain time update validated asset", testOnTick)
    81  	t.Run("on chain time update validated asset - non validator", testOnTickNonValidator)
    82  }
    83  
    84  func testStartErrorDuplicate(t *testing.T) {
    85  	erc := getTestWitness(t)
    86  	defer erc.ctrl.Finish()
    87  	defer erc.Stop()
    88  
    89  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
    90  	res := testRes{"resource-id-1", func() error {
    91  		return nil
    92  	}}
    93  	checkUntil := erc.startTime.Add(700 * time.Second)
    94  	cb := func(interface{}, bool) {}
    95  
    96  	err := erc.StartCheck(res, cb, checkUntil)
    97  	assert.NoError(t, err)
    98  	err = erc.StartCheck(res, cb, checkUntil)
    99  	assert.EqualError(t, err, validators.ErrResourceDuplicate.Error())
   100  }
   101  
   102  func testStartErrorCheckFailed(t *testing.T) {
   103  	erc := getTestWitness(t)
   104  	defer erc.ctrl.Finish()
   105  	defer erc.Stop()
   106  
   107  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   108  	res := testRes{"resource-id-1", func() error {
   109  		return nil
   110  	}}
   111  	checkUntil := erc.startTime.Add(31 * 24 * time.Hour)
   112  	cb := func(interface{}, bool) {}
   113  
   114  	err := erc.StartCheck(res, cb, checkUntil)
   115  	assert.EqualError(t, err, validators.ErrCheckUntilInvalid.Error())
   116  }
   117  
   118  func testStartOK(t *testing.T) {
   119  	erc := getTestWitness(t)
   120  	defer erc.ctrl.Finish()
   121  	defer erc.Stop()
   122  
   123  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   124  	res := testRes{"resource-id-1", func() error {
   125  		return nil
   126  	}}
   127  	checkUntil := erc.startTime.Add(700 * time.Second)
   128  	cb := func(interface{}, bool) {}
   129  
   130  	err := erc.StartCheck(res, cb, checkUntil)
   131  	assert.NoError(t, err)
   132  }
   133  
   134  func testNodeVoteInvalidProposalReference(t *testing.T) {
   135  	erc := getTestWitness(t)
   136  	defer erc.ctrl.Finish()
   137  	defer erc.Stop()
   138  
   139  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   140  	res := testRes{"resource-id-1", func() error {
   141  		return nil
   142  	}}
   143  	checkUntil := erc.startTime.Add(700 * time.Second)
   144  	cb := func(interface{}, bool) {}
   145  
   146  	err := erc.StartCheck(res, cb, checkUntil)
   147  	assert.NoError(t, err)
   148  
   149  	pubKey := newPublicKey("somepubkey")
   150  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: "bad-id"}, pubKey)
   151  	assert.EqualError(t, err, validators.ErrInvalidResourceIDForNodeVote.Error())
   152  }
   153  
   154  func testNodeVoteNotAValidator(t *testing.T) {
   155  	erc := getTestWitness(t)
   156  	defer erc.ctrl.Finish()
   157  	defer erc.Stop()
   158  
   159  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   160  	res := testRes{"resource-id-1", func() error {
   161  		return nil
   162  	}}
   163  	checkUntil := erc.startTime.Add(700 * time.Second)
   164  	cb := func(interface{}, bool) {}
   165  
   166  	err := erc.StartCheck(res, cb, checkUntil)
   167  	assert.NoError(t, err)
   168  
   169  	pubKey := newPublicKey("somepubkey")
   170  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(false)
   171  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   172  	assert.EqualError(t, err, validators.ErrVoteFromNonValidator.Error())
   173  }
   174  
   175  func testNodeVoteOK(t *testing.T) {
   176  	erc := getTestWitness(t)
   177  	defer erc.ctrl.Finish()
   178  	defer erc.Stop()
   179  
   180  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   181  
   182  	res := testRes{"resource-id-1", func() error {
   183  		return nil
   184  	}}
   185  	checkUntil := erc.startTime.Add(700 * time.Second)
   186  	cb := func(interface{}, bool) {}
   187  
   188  	err := erc.StartCheck(res, cb, checkUntil)
   189  	assert.NoError(t, err)
   190  
   191  	pubKey := newPublicKey("somepubkey")
   192  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true)
   193  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   194  	assert.NoError(t, err)
   195  }
   196  
   197  func testNodeVoteDuplicateVote(t *testing.T) {
   198  	erc := getTestWitness(t)
   199  	defer erc.ctrl.Finish()
   200  	defer erc.Stop()
   201  
   202  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   203  	res := testRes{"resource-id-1", func() error {
   204  		return nil
   205  	}}
   206  	checkUntil := erc.startTime.Add(700 * time.Second)
   207  	cb := func(interface{}, bool) {}
   208  
   209  	err := erc.StartCheck(res, cb, checkUntil)
   210  	assert.NoError(t, err)
   211  
   212  	// first vote, all good
   213  	pubKey := newPublicKey("somepubkey")
   214  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true)
   215  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   216  	require.NoError(t, err)
   217  
   218  	// second vote, bad
   219  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true)
   220  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   221  	require.EqualError(t, err, validators.ErrDuplicateVoteFromNode.Error())
   222  }
   223  
   224  func TestVoteMajorityCalculation(t *testing.T) {
   225  	erc := getTestWitness(t)
   226  	defer erc.ctrl.Finish()
   227  	defer erc.Stop()
   228  	erc.Witness.OnDefaultValidatorsVoteRequiredUpdate(context.Background(), num.DecimalFromFloat(0.67))
   229  	selfPubKey := "b7ee437dc100d642"
   230  
   231  	erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(500))
   232  	erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100))
   233  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   234  
   235  	ch := make(chan struct{}, 1)
   236  	defer close(ch)
   237  	res := testRes{"resource-id-1", func() error {
   238  		ch <- struct{}{}
   239  		return nil
   240  	}}
   241  	checkUntil := erc.startTime.Add(700 * time.Second)
   242  	cb := func(interface{}, bool) {
   243  		// unblock chanel listen to finish test
   244  		ch <- struct{}{}
   245  	}
   246  
   247  	err := erc.StartCheck(res, cb, checkUntil)
   248  	assert.NoError(t, err)
   249  
   250  	// first wait once for the asset to be validated
   251  	<-ch
   252  
   253  	wg := sync.WaitGroup{}
   254  	wg.Add(2)
   255  	// first on chain time update, we send our own vote
   256  	erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Do(func(_ context.Context, _ txn.Command, _ proto.Message, _ func(string, error), _ *backoff.ExponentialBackOff) {
   257  		wg.Done()
   258  	})
   259  	newNow := erc.startTime.Add(1 * time.Second)
   260  	erc.OnTick(context.Background(), newNow)
   261  
   262  	// then we propagate our own vote
   263  	pubKey := newPublicKey(selfPubKey)
   264  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true).Do(func(_ string) {
   265  		wg.Done()
   266  	})
   267  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   268  	assert.NoError(t, err)
   269  
   270  	for i := 0; i < 3; i++ {
   271  		// second vote from another validator
   272  		othPubKey := newPublicKey(crypto.RandomHash())
   273  		wg.Add(1)
   274  		erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true).Do(func(_ string) {
   275  			wg.Done()
   276  		})
   277  		err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey)
   278  		assert.NoError(t, err)
   279  	}
   280  
   281  	// we have 4 votes, that should suffice to pass
   282  	// call onTick again to get the callback called
   283  	newNow = newNow.Add(1 * time.Second)
   284  	erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(4).Return(true)
   285  	erc.OnTick(context.Background(), newNow)
   286  
   287  	// block to wait for the result
   288  	<-ch
   289  	wg.Wait()
   290  }
   291  
   292  func testOnTick(t *testing.T) {
   293  	erc := getTestWitness(t)
   294  	defer erc.ctrl.Finish()
   295  	defer erc.Stop()
   296  
   297  	selfPubKey := "b7ee437dc100d642"
   298  
   299  	erc.Witness.OnDefaultValidatorsVoteRequiredUpdate(context.Background(), num.DecimalFromFloat(0.67))
   300  	erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(298))
   301  	erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100))
   302  	erc.top.EXPECT().IsValidator().AnyTimes().Return(true)
   303  
   304  	wg := sync.WaitGroup{}
   305  	ch := make(chan struct{}, 1)
   306  	defer close(ch)
   307  	res := testRes{"resource-id-1", func() error {
   308  		ch <- struct{}{}
   309  		return nil
   310  	}}
   311  	checkUntil := erc.startTime.Add(700 * time.Second)
   312  	cb := func(interface{}, bool) {
   313  		// unblock chanel listen to finish test
   314  		ch <- struct{}{}
   315  	}
   316  
   317  	err := erc.StartCheck(res, cb, checkUntil)
   318  	assert.NoError(t, err)
   319  
   320  	// first wait once for the asset to be validated
   321  	<-ch
   322  
   323  	// first on chain time update, we send our own vote
   324  	wg.Add(1)
   325  	erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Do(func(_ context.Context, _ txn.Command, _ proto.Message, _ func(string, error), _ *backoff.ExponentialBackOff) {
   326  		wg.Done()
   327  	})
   328  	newNow := erc.startTime.Add(1 * time.Second)
   329  	erc.OnTick(context.Background(), newNow)
   330  
   331  	// then we propagate our own vote
   332  	pubKey := newPublicKey(selfPubKey)
   333  	wg.Add(1)
   334  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true).Do(func(_ string) {
   335  		wg.Done()
   336  	})
   337  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   338  	assert.NoError(t, err)
   339  
   340  	// second vote from another validator
   341  	othPubKey := newPublicKey("somepubkey")
   342  	wg.Add(1)
   343  	erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true).Do(func(_ string) {
   344  		wg.Done()
   345  	})
   346  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey)
   347  	assert.NoError(t, err)
   348  
   349  	// call onTick again to get the callback called
   350  	newNow = newNow.Add(1 * time.Second)
   351  	erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(2).Return(true)
   352  	erc.OnTick(context.Background(), newNow)
   353  
   354  	// block to wait for the result
   355  	<-ch
   356  	wg.Wait()
   357  }
   358  
   359  func testOnTickNonValidator(t *testing.T) {
   360  	erc := getTestWitness(t)
   361  	defer erc.ctrl.Finish()
   362  	defer erc.Stop()
   363  
   364  	selfPubKey := "b7ee437dc100d642"
   365  
   366  	erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(298))
   367  	erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100))
   368  	erc.top.EXPECT().IsValidator().AnyTimes().Return(false)
   369  
   370  	res := testRes{"resource-id-1", func() error {
   371  		return nil
   372  	}}
   373  
   374  	checkUntil := erc.startTime.Add(700 * time.Second)
   375  	cb := func(interface{}, bool) {}
   376  
   377  	err := erc.StartCheck(res, cb, checkUntil)
   378  	assert.NoError(t, err)
   379  
   380  	// first on chain time update, we send our own vote
   381  	erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
   382  	newNow := erc.startTime.Add(1 * time.Second)
   383  	erc.OnTick(context.Background(), newNow)
   384  
   385  	// then we propagate our own vote
   386  	pubKey := newPublicKey(selfPubKey)
   387  	erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true)
   388  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey)
   389  	assert.NoError(t, err)
   390  
   391  	// second vote from another validator
   392  	othPubKey := newPublicKey("somepubkey")
   393  	erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true)
   394  	err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey)
   395  	assert.NoError(t, err)
   396  
   397  	// call onTick again to get the callback called
   398  	newNow = newNow.Add(1 * time.Second)
   399  	erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(2).Return(true)
   400  	erc.OnTick(context.Background(), newNow)
   401  }
   402  
   403  type testRes struct {
   404  	id    string
   405  	check func() error
   406  }
   407  
   408  func (t testRes) GetChainID() string { return "" }
   409  func (t testRes) GetID() string      { return t.id }
   410  func (t testRes) GetType() commandspb.NodeVote_Type {
   411  	return commandspb.NodeVote_TYPE_FUNDS_DEPOSITED
   412  }
   413  func (t testRes) Check(ctx context.Context) error { return t.check() }
   414  
   415  func newPublicKey(k string) crypto.PublicKey {
   416  	pubKeyB := []byte(k)
   417  	return crypto.NewPublicKey(hex.EncodeToString(pubKeyB), pubKeyB)
   418  }