code.vegaprotocol.io/vega@v0.79.0/core/netparams/netparams_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 netparams_test
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"testing"
    22  	"time"
    23  
    24  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    25  	"code.vegaprotocol.io/vega/core/netparams"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/logging"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  type testNetParams struct {
    35  	*netparams.Store
    36  	ctrl   *gomock.Controller
    37  	broker *bmocks.MockBroker
    38  }
    39  
    40  func getTestNetParams(t *testing.T) *testNetParams {
    41  	t.Helper()
    42  	ctrl := gomock.NewController(t)
    43  	broker := bmocks.NewMockBroker(ctrl)
    44  	store := netparams.New(logging.NewTestLogger(), netparams.NewDefaultConfig(), broker)
    45  
    46  	return &testNetParams{
    47  		Store:  store,
    48  		ctrl:   ctrl,
    49  		broker: broker,
    50  	}
    51  }
    52  
    53  func TestNetParams(t *testing.T) {
    54  	t.Run("test validate - succes", testValidateSuccess)
    55  	t.Run("test validate - unknown key", testValidateUnknownKey)
    56  	t.Run("test validate - validation failed", testValidateValidationFailed)
    57  	t.Run("test update - success", testUpdateSuccess)
    58  	t.Run("test update - unknown key", testUpdateUnknownKey)
    59  	t.Run("test update - validation failed", testUpdateValidationFailed)
    60  	t.Run("test exists - success", testExistsSuccess)
    61  	t.Run("test exists - failure", testExistsFailure)
    62  	t.Run("get decimal", testGetDecimal)
    63  	t.Run("get duration", testGetDuration)
    64  	t.Run("dispatch after update", testDispatchAfterUpdate)
    65  	t.Run("register dispatch function - failure", testRegisterDispatchFunctionFailure)
    66  }
    67  
    68  func TestCheckpoint(t *testing.T) {
    69  	t.Run("test get snapshot not empty", testNonEmptyCheckpoint)
    70  	t.Run("test get snapshot not empty with overwrite", testNonEmptyCheckpointWithOverWrite)
    71  	t.Run("test get snapshot invalid", testInvalidCheckpoint)
    72  	t.Run("test notification is sent after checkpoint load", testCheckpointNotificationsDelivered)
    73  }
    74  
    75  func testRegisterDispatchFunctionFailure(t *testing.T) {
    76  	netp := getTestNetParams(t)
    77  	defer netp.ctrl.Finish()
    78  
    79  	err := netp.Watch(
    80  		netparams.WatchParam{
    81  			Param:   netparams.GovernanceProposalAssetMaxClose,
    82  			Watcher: func(s string) error { return nil },
    83  		},
    84  	)
    85  
    86  	assert.EqualError(t, err, "governance.proposal.asset.maxClose: invalid type, expected func(context.Context, time.Duration) error")
    87  }
    88  
    89  func testDispatchAfterUpdate(t *testing.T) {
    90  	netp := getTestNetParams(t)
    91  	defer netp.ctrl.Finish()
    92  
    93  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    94  	newDuration := "10s"
    95  	var wasCalled bool
    96  	f := func(_ context.Context, d time.Duration) error {
    97  		assert.Equal(t, d, 10*time.Second)
    98  		wasCalled = true
    99  		return nil
   100  	}
   101  
   102  	err := netp.Watch(
   103  		netparams.WatchParam{
   104  			Param:   netparams.GovernanceProposalAssetMaxClose,
   105  			Watcher: f,
   106  		},
   107  	)
   108  
   109  	assert.NoError(t, err)
   110  
   111  	err = netp.Update(context.Background(), netparams.GovernanceProposalAssetMaxClose, newDuration)
   112  	assert.NoError(t, err)
   113  
   114  	netp.DispatchChanges(context.Background())
   115  	assert.True(t, wasCalled)
   116  }
   117  
   118  func testValidateSuccess(t *testing.T) {
   119  	netp := getTestNetParams(t)
   120  	defer netp.ctrl.Finish()
   121  
   122  	err := netp.Validate(netparams.GovernanceProposalMarketMinClose, "10h")
   123  	assert.NoError(t, err)
   124  }
   125  
   126  func testValidateUnknownKey(t *testing.T) {
   127  	netp := getTestNetParams(t)
   128  	defer netp.ctrl.Finish()
   129  
   130  	err := netp.Validate("not.a.valid.key", "10h")
   131  	assert.EqualError(t, err, netparams.ErrUnknownKey.Error())
   132  }
   133  
   134  func testValidateValidationFailed(t *testing.T) {
   135  	netp := getTestNetParams(t)
   136  	defer netp.ctrl.Finish()
   137  
   138  	err := netp.Validate(netparams.GovernanceProposalMarketMinClose, "asdasdasd")
   139  	assert.Error(t, err)
   140  	assert.Contains(t, err.Error(), "time: invalid duration")
   141  }
   142  
   143  func testUpdateSuccess(t *testing.T) {
   144  	netp := getTestNetParams(t)
   145  	defer netp.ctrl.Finish()
   146  
   147  	// get the original default value
   148  	ov, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   149  	assert.NoError(t, err)
   150  	assert.NotEmpty(t, ov)
   151  	assert.NotEqual(t, ov, "10h")
   152  
   153  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   154  
   155  	err = netp.Update(
   156  		context.Background(), netparams.GovernanceProposalMarketMinClose, "10h")
   157  	assert.NoError(t, err)
   158  
   159  	nv, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   160  	assert.NoError(t, err)
   161  	assert.NotEmpty(t, nv)
   162  	assert.NotEqual(t, nv, ov)
   163  	assert.Equal(t, nv, "10h")
   164  }
   165  
   166  func testUpdateUnknownKey(t *testing.T) {
   167  	netp := getTestNetParams(t)
   168  	defer netp.ctrl.Finish()
   169  
   170  	err := netp.Update(context.Background(), "not.a.valid.key", "10h")
   171  	assert.EqualError(t, err, netparams.ErrUnknownKey.Error())
   172  }
   173  
   174  func testUpdateValidationFailed(t *testing.T) {
   175  	netp := getTestNetParams(t)
   176  	defer netp.ctrl.Finish()
   177  
   178  	err := netp.Update(
   179  		context.Background(), netparams.GovernanceProposalMarketMinClose, "asdadasd")
   180  	assert.Error(t, err)
   181  	assert.Contains(t, err.Error(), "time: invalid duration")
   182  }
   183  
   184  func testExistsSuccess(t *testing.T) {
   185  	netp := getTestNetParams(t)
   186  	defer netp.ctrl.Finish()
   187  
   188  	ok := netp.Exists(netparams.GovernanceProposalMarketMinClose)
   189  	assert.True(t, ok)
   190  }
   191  
   192  func testExistsFailure(t *testing.T) {
   193  	netp := getTestNetParams(t)
   194  	defer netp.ctrl.Finish()
   195  
   196  	ok := netp.Exists("not.valid")
   197  	assert.False(t, ok)
   198  }
   199  
   200  func testGetDecimal(t *testing.T) {
   201  	netp := getTestNetParams(t)
   202  	defer netp.ctrl.Finish()
   203  
   204  	_, err := netp.GetDecimal(netparams.GovernanceProposalUpdateNetParamRequiredMajority)
   205  	assert.NoError(t, err)
   206  	_, err = netp.GetInt(netparams.GovernanceProposalAssetMaxClose)
   207  	assert.EqualError(t, err, "not an int value")
   208  }
   209  
   210  func testGetDuration(t *testing.T) {
   211  	netp := getTestNetParams(t)
   212  	defer netp.ctrl.Finish()
   213  
   214  	_, err := netp.GetDuration(netparams.GovernanceProposalAssetMaxClose)
   215  	assert.NoError(t, err)
   216  	_, err = netp.GetDuration(netparams.GovernanceProposalAssetMinProposerBalance)
   217  	assert.EqualError(t, err, "not a time.Duration value")
   218  }
   219  
   220  func testNonEmptyCheckpoint(t *testing.T) {
   221  	netp := getTestNetParams(t)
   222  	defer netp.ctrl.Finish()
   223  	ctx := context.Background()
   224  
   225  	// get the original default value
   226  	ov, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   227  	assert.NoError(t, err)
   228  	assert.NotEmpty(t, ov)
   229  	assert.NotEqual(t, ov, "10h")
   230  
   231  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   232  
   233  	err = netp.Update(ctx, netparams.GovernanceProposalMarketMinClose, "10h")
   234  	assert.NoError(t, err)
   235  
   236  	nv, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   237  	assert.NoError(t, err)
   238  	assert.NotEmpty(t, nv)
   239  	assert.NotEqual(t, nv, ov)
   240  	assert.Equal(t, nv, "10h")
   241  
   242  	data, err := netp.Checkpoint()
   243  	require.NoError(t, err)
   244  	require.NotEmpty(t, data)
   245  
   246  	// now try and load the checkpoint
   247  	netp2 := getTestNetParams(t)
   248  	defer netp2.ctrl.Finish()
   249  
   250  	// ensure the state != checkpoint we took
   251  	ov2, err := netp2.Get(netparams.GovernanceProposalMarketMinClose)
   252  	assert.NoError(t, err)
   253  	assert.NotEmpty(t, ov2)
   254  	assert.NotEqual(t, ov2, "10h")
   255  	require.Equal(t, ov, ov2)
   256  
   257  	netp2.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   258  	require.NoError(t, netp2.Load(ctx, data))
   259  
   260  	nv2, err := netp2.Get(netparams.GovernanceProposalMarketMinClose)
   261  	assert.NoError(t, err)
   262  	assert.NotEmpty(t, nv2)
   263  	assert.NotEqual(t, nv2, ov)
   264  	assert.Equal(t, nv, nv2)
   265  
   266  	// make sure that, once restored, the same checkpoint data is restored
   267  	data2, err := netp2.Checkpoint()
   268  	require.NoError(t, err)
   269  	require.EqualValues(t, data, data2)
   270  }
   271  
   272  func testInvalidCheckpoint(t *testing.T) {
   273  	netp := getTestNetParams(t)
   274  	defer netp.ctrl.Finish()
   275  	ctx := context.Background()
   276  
   277  	data, err := netp.Checkpoint()
   278  	require.NoError(t, err)
   279  	require.NotEmpty(t, data)
   280  
   281  	data = append(data, []byte("foobar")...) // corrupt the data
   282  	require.Error(t, netp.Load(ctx, data))
   283  }
   284  
   285  func testCheckpointNotificationsDelivered(t *testing.T) {
   286  	netp := getTestNetParams(t)
   287  	defer netp.ctrl.Finish()
   288  	ctx := context.Background()
   289  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   290  
   291  	counter := 0
   292  	countNotificationsFunc := func(_ context.Context, minAmount num.Decimal) error {
   293  		counter++
   294  		return nil
   295  	}
   296  
   297  	netp.Watch(
   298  		netparams.WatchParam{
   299  			Param:   netparams.DelegationMinAmount,
   300  			Watcher: countNotificationsFunc,
   301  		},
   302  	)
   303  
   304  	err := netp.Update(ctx, netparams.DelegationMinAmount, "2.0")
   305  	assert.NoError(t, err)
   306  
   307  	netp.OnTick(ctx, time.Now())
   308  	require.Equal(t, 1, counter)
   309  
   310  	cp, err := netp.Checkpoint()
   311  	require.NoError(t, err)
   312  
   313  	loadNp := getTestNetParams(t)
   314  	defer loadNp.ctrl.Finish()
   315  	loadNp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   316  	loadNp.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   317  
   318  	var loadMinAmount num.Decimal
   319  	loadCountNotificationsFunc := func(_ context.Context, minAmount num.Decimal) error {
   320  		loadMinAmount = minAmount
   321  		return nil
   322  	}
   323  	loadNp.Watch(
   324  		netparams.WatchParam{
   325  			Param:   netparams.DelegationMinAmount,
   326  			Watcher: loadCountNotificationsFunc,
   327  		},
   328  	)
   329  	loadNp.Load(ctx, cp)
   330  	loadNp.OnTick(ctx, time.Now())
   331  	require.Equal(t, "2", loadMinAmount.String())
   332  }
   333  
   334  func testNonEmptyCheckpointWithOverWrite(t *testing.T) {
   335  	netp := getTestNetParams(t)
   336  	defer netp.ctrl.Finish()
   337  	ctx := context.Background()
   338  
   339  	// get the original default value
   340  	ov, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   341  	assert.NoError(t, err)
   342  	assert.NotEmpty(t, ov)
   343  	assert.NotEqual(t, ov, "10h")
   344  
   345  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   346  
   347  	err = netp.Update(ctx, netparams.GovernanceProposalMarketMinClose, "10h")
   348  	assert.NoError(t, err)
   349  
   350  	nv, err := netp.Get(netparams.GovernanceProposalMarketMinClose)
   351  	assert.NoError(t, err)
   352  	assert.NotEmpty(t, nv)
   353  	assert.NotEqual(t, nv, ov)
   354  	assert.Equal(t, nv, "10h")
   355  
   356  	data, err := netp.Checkpoint()
   357  	require.NoError(t, err)
   358  	require.NotEmpty(t, data)
   359  
   360  	// now try and load the checkpoint
   361  	netp2 := getTestNetParams(t)
   362  	defer netp2.ctrl.Finish()
   363  	netp2.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   364  	netp2.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   365  
   366  	genesis := map[string]interface{}{
   367  		"network_parameters": map[string]string{
   368  			"market.liquidity.successorLaunchWindowLength": "1h",
   369  		},
   370  		"network_parameters_checkpoint_overwrite": []string{"market.liquidity.successorLaunchWindowLength"},
   371  	}
   372  
   373  	buf, err := json.Marshal(genesis)
   374  	assert.NoError(t, err)
   375  
   376  	assert.NoError(t, netp2.UponGenesis(context.Background(), buf))
   377  
   378  	// ensure the state != checkpoint we took
   379  	ov2, err := netp2.Get(netparams.GovernanceProposalMarketMinClose)
   380  	assert.NoError(t, err)
   381  	assert.NotEmpty(t, ov2)
   382  	assert.NotEqual(t, ov2, "10h")
   383  	require.Equal(t, ov, ov2)
   384  
   385  	require.NoError(t, netp2.Load(ctx, data))
   386  
   387  	nv2, err := netp2.Get(netparams.GovernanceProposalMarketMinClose)
   388  	assert.NoError(t, err)
   389  	assert.NotEmpty(t, nv2)
   390  	assert.NotEqual(t, nv2, ov)
   391  	assert.Equal(t, nv, nv2)
   392  
   393  	// make sure that, once restored, the same checkpoint data is restored
   394  	_, err = netp2.Checkpoint()
   395  	require.NoError(t, err)
   396  }
   397  
   398  func TestCrossNetParamUpdates(t *testing.T) {
   399  	netp := getTestNetParams(t)
   400  	defer netp.ctrl.Finish()
   401  
   402  	// min and max durations are defined such that min must be less than max, so lets first verify that the constraint holds
   403  	min, err := netp.GetDuration(netparams.MarketAuctionMinimumDuration)
   404  	require.NoError(t, err)
   405  	require.Equal(t, time.Minute*30, min)
   406  
   407  	max, err := netp.GetDuration(netparams.MarketAuctionMaximumDuration)
   408  	require.NoError(t, err)
   409  	require.Equal(t, time.Hour*168, max)
   410  
   411  	// now lets try to update max to a valid value (1s) with respect to its own validation rules but would invalidate the invariant of max > min
   412  	err = netp.Validate(netparams.MarketAuctionMaximumDuration, "1s")
   413  	require.Equal(t, "unable to validate market.auction.maximumDuration: expect > 30m0s (market.auction.minimumDuration) got 1s", err.Error())
   414  
   415  	// now lets change the maximum to be 12h so that we can cross it with the minimum
   416  	err = netp.Validate(netparams.MarketAuctionMaximumDuration, "12h")
   417  	require.NoError(t, err)
   418  
   419  	netp.broker.EXPECT().Send(gomock.Any()).Times(1)
   420  	netp.Update(context.Background(), netparams.MarketAuctionMaximumDuration, "12h")
   421  
   422  	// now lets try to update min to a valid value (13h) with respect to its own validation rules but would invalidate the invariant of max > min
   423  	err = netp.Validate(netparams.MarketAuctionMinimumDuration, "13h")
   424  	require.Equal(t, "unable to validate market.auction.minimumDuration: expect < 12h0m0s (market.auction.maximumDuration) got 13h0m0s", err.Error())
   425  }
   426  
   427  func TestCrossNetParamUpdatesInGenesis(t *testing.T) {
   428  	netp := getTestNetParams(t)
   429  	defer netp.ctrl.Finish()
   430  
   431  	genesis1 := map[string]interface{}{
   432  		"network_parameters": map[string]string{
   433  			"network.validators.tendermint.number":        "5",
   434  			"network.validators.multisig.numberOfSigners": "5",
   435  		},
   436  		"network_parameters_checkpoint_overwrite": []string{},
   437  	}
   438  
   439  	netp.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   440  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   441  	buf, err := json.Marshal(genesis1)
   442  	require.NoError(t, err)
   443  	require.NoError(t, netp.UponGenesis(context.Background(), buf))
   444  
   445  	genesis2 := map[string]interface{}{
   446  		"network_parameters": map[string]string{
   447  			"network.validators.multisig.numberOfSigners": "5",
   448  			"network.validators.tendermint.number":        "5",
   449  		},
   450  		"network_parameters_checkpoint_overwrite": []string{},
   451  	}
   452  
   453  	netp.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   454  	netp.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   455  	buf, err = json.Marshal(genesis2)
   456  	require.NoError(t, err)
   457  	require.NoError(t, netp.UponGenesis(context.Background(), buf))
   458  }
   459  
   460  func TestDefaultStateHidesDeprecated(t *testing.T) {
   461  	st := netparams.DefaultGenesisState()
   462  
   463  	for v := range st {
   464  		_, ok := netparams.Deprecated[v]
   465  		assert.False(t, ok)
   466  	}
   467  }