code.vegaprotocol.io/vega@v0.79.0/core/governance/engine_new_net_param_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 governance_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/netparams"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    27  
    28  	"github.com/golang/mock/gomock"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  // TestProposalForNetParamInvalidOnProposal verifies that an invalid value with respect to current values (i.e. crossing with the other current value) fails at submission time.
    33  func TestProposalForNetParamInvalidOnProposal(t *testing.T) {
    34  	now := time.Now()
    35  
    36  	eng := getTestEngine(t, now)
    37  
    38  	party := eng.newValidParty("a-valid-party", 1)
    39  
    40  	// propose a min time that is greater than the max time, this should be invalid upon proposal and should be immediately rejected
    41  	p1 := eng.newProposalForNetParam(party.Id, netparams.MarketAuctionMinimumDuration, "169h", now.Add(48*time.Hour))
    42  
    43  	// setup
    44  	eng.broker.EXPECT().Send(gomock.Any()).Times(1)
    45  
    46  	_, err := eng.submitProposal(t, p1)
    47  
    48  	// then
    49  	require.Equal(t, "unable to validate market.auction.minimumDuration: expect <= 24h0m0s got 169h0m0s, expect < 168h0m0s (market.auction.maximumDuration) got 169h0m0s", err.Error())
    50  }
    51  
    52  // TestProposalForNetParamCrossingOnUpdate submits two crossing proposals with the same enactment time. In this case the proposals pass the initial validation but also
    53  // pass the enactment validation and only fail at the last step of updating the param. The proposal will be marked as faild and only one of the values will go through.
    54  func TestProposalForNetParamCrossingOnUpdate(t *testing.T) {
    55  	eng := getTestEngine(t, time.Now())
    56  
    57  	party1 := eng.newValidParty("a-valid-party", 1)
    58  	party2 := eng.newValidParty("another-valid-party", 1)
    59  
    60  	now := eng.tsvc.GetTimeNow()
    61  	date1 := now.Add(5 * 24 * time.Hour)
    62  
    63  	// lower the max first, this should go through
    64  	p1 := eng.newProposalForNetParam(party1.Id, netparams.MarketAuctionMaximumDuration, "10h", date1)
    65  
    66  	// setup
    67  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    68  	eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
    69  
    70  	_, err := eng.submitProposal(t, p1)
    71  	require.NoError(t, err)
    72  
    73  	// now increase the min above the max, this should be valid at the time of proposal
    74  	p2 := eng.newProposalForNetParam(party2.Id, netparams.MarketAuctionMinimumDuration, "11h", date1)
    75  
    76  	_, err = eng.submitProposal(t, p2)
    77  	require.NoError(t, err)
    78  
    79  	// now get them enacted in order, the expected result is that the one enacted first gets through and the latter gets rejected
    80  	// i.e. min = 5s, max = 10h
    81  
    82  	eng.accounts.EXPECT().GetStakingAssetTotalSupply().Times(2).Return(num.NewUint(9))
    83  	eng.ensureTokenBalanceForParty(t, party1.Id, 1)
    84  	eng.ensureTokenBalanceForParty(t, party2.Id, 1)
    85  
    86  	// vote for first proposal
    87  	voter1 := vgrand.RandomStr(5)
    88  	eng.ensureTokenBalanceForParty(t, voter1, 7)
    89  	err = eng.addYesVote(t, voter1, p1.ID)
    90  	require.NoError(t, err)
    91  
    92  	// vote for second proposal
    93  	voter2 := vgrand.RandomStr(5)
    94  	eng.ensureTokenBalanceForParty(t, voter2, 7)
    95  	err = eng.addYesVote(t, voter2, p2.ID)
    96  	require.NoError(t, err)
    97  
    98  	// given
    99  	afterEnactment := time.Unix(p1.Terms.EnactmentTimestamp, 0).Add(time.Second)
   100  
   101  	// time to enact, expect both of them to be enacted because at the time of check none has updated yet
   102  	enacted, _ := eng.OnTick(context.Background(), afterEnactment)
   103  	require.Equal(t, 2, len(enacted))
   104  
   105  	// update enacted - first is expected to pass
   106  	err = eng.netp.Update(context.Background(), enacted[0].UpdateNetworkParameter().Key, enacted[0].UpdateNetworkParameter().Value)
   107  	require.NoError(t, err)
   108  
   109  	// second expected to fail the validation as the first has already been updated
   110  	err = eng.netp.Update(context.Background(), enacted[1].UpdateNetworkParameter().Key, enacted[1].UpdateNetworkParameter().Value)
   111  	require.Equal(t, "unable to update market.auction.minimumDuration: expect < 10h0m0s (market.auction.maximumDuration) got 11h0m0s", err.Error())
   112  
   113  	max, err := eng.netp.Get(netparams.MarketAuctionMaximumDuration)
   114  	require.Equal(t, "10h", max)
   115  	require.NoError(t, err)
   116  
   117  	min, err := eng.netp.Get(netparams.MarketAuctionMinimumDuration)
   118  	require.Equal(t, "30m0s", min)
   119  	require.NoError(t, err)
   120  }
   121  
   122  // TestProposalForNetParamCrossingAtEnactment submits two crossing proposals such that one goes in before the other. In this case the first proposal passes and will get updated while
   123  // the second proposal will fail pre-enactment.
   124  func TestProposalForNetParamCrossingAtEnactment(t *testing.T) {
   125  	eng := getTestEngine(t, time.Now())
   126  
   127  	party1 := eng.newValidParty("a-valid-party", 1)
   128  	party2 := eng.newValidParty("another-valid-party", 1)
   129  
   130  	now := eng.tsvc.GetTimeNow()
   131  
   132  	date1 := now.Add(5 * 24 * time.Hour)
   133  	date2 := date1.Add(time.Hour)
   134  
   135  	// lower the max first, this should go through
   136  	p1 := eng.newProposalForNetParam(party1.Id, netparams.MarketAuctionMaximumDuration, "10h", date1)
   137  
   138  	// setup
   139  	eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   140  	eng.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   141  
   142  	_, err := eng.submitProposal(t, p1)
   143  	require.NoError(t, err)
   144  
   145  	// now increase the min above the max, this should be valid at the time of proposal
   146  	p2 := eng.newProposalForNetParam(party2.Id, netparams.MarketAuctionMinimumDuration, "11h", date2)
   147  
   148  	_, err = eng.submitProposal(t, p2)
   149  	require.NoError(t, err)
   150  
   151  	// now get them enacted in order, the expected result is that the one enacted first gets through and the latter gets rejected
   152  	// i.e. min = 5s, max = 10h
   153  
   154  	eng.accounts.EXPECT().GetStakingAssetTotalSupply().Times(2).Return(num.NewUint(9))
   155  	eng.ensureTokenBalanceForParty(t, party1.Id, 1)
   156  	eng.ensureTokenBalanceForParty(t, party2.Id, 1)
   157  
   158  	// vote for first proposal
   159  	voter1 := vgrand.RandomStr(5)
   160  	eng.ensureTokenBalanceForParty(t, voter1, 7)
   161  	err = eng.addYesVote(t, voter1, p1.ID)
   162  	require.NoError(t, err)
   163  
   164  	// vote for second proposal
   165  	voter2 := vgrand.RandomStr(5)
   166  	eng.ensureTokenBalanceForParty(t, voter2, 7)
   167  	err = eng.addYesVote(t, voter2, p2.ID)
   168  	require.NoError(t, err)
   169  
   170  	// move time to after the enactment time of the first proposal
   171  	afterEnactment1 := time.Unix(p1.Terms.EnactmentTimestamp, 0).Add(time.Second)
   172  
   173  	// time to enact, expect both of them to be enacted because at the time of check none has updated yet
   174  	enacted, _ := eng.OnTick(context.Background(), afterEnactment1)
   175  	require.Equal(t, 1, len(enacted))
   176  
   177  	// update enacted - first is expected to pass
   178  	err = eng.netp.Update(context.Background(), enacted[0].UpdateNetworkParameter().Key, enacted[0].UpdateNetworkParameter().Value)
   179  	require.NoError(t, err)
   180  
   181  	// move time to after the enactment time of the second proposal
   182  	afterEnactment2 := time.Unix(p2.Terms.EnactmentTimestamp, 0).Add(time.Second)
   183  
   184  	// time to enact, expect both of them to be enacted because at the time of check none has updated yet
   185  	enacted, _ = eng.OnTick(context.Background(), afterEnactment2)
   186  	require.Equal(t, 0, len(enacted))
   187  
   188  	max, err := eng.netp.Get(netparams.MarketAuctionMaximumDuration)
   189  	require.Equal(t, "10h", max)
   190  	require.NoError(t, err)
   191  
   192  	min, err := eng.netp.Get(netparams.MarketAuctionMinimumDuration)
   193  	require.Equal(t, "30m0s", min)
   194  	require.NoError(t, err)
   195  
   196  	eng.OnTick(context.Background(), afterEnactment1.Add(1*time.Second))
   197  }
   198  
   199  func (e *tstEngine) newProposalForNetParam(partyID, key, value string, now time.Time) types.Proposal {
   200  	id := e.newProposalID()
   201  	return types.Proposal{
   202  		ID:        id,
   203  		Reference: "ref-" + id,
   204  		Party:     partyID,
   205  		State:     types.ProposalStateOpen,
   206  		Terms: &types.ProposalTerms{
   207  			ClosingTimestamp:    now.Add(48 * time.Hour).Unix(),
   208  			EnactmentTimestamp:  now.Add(2 * 48 * time.Hour).Unix(),
   209  			ValidationTimestamp: now.Add(1 * time.Hour).Unix(),
   210  			Change:              newNetParamTerms(key, value),
   211  		},
   212  		Rationale: &types.ProposalRationale{
   213  			Description: "some description",
   214  		},
   215  	}
   216  }