code.vegaprotocol.io/vega@v0.79.0/core/monitor/snapshot_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 monitor_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/datasource"
    24  	"code.vegaprotocol.io/vega/core/monitor"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/libs/crypto"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func getMarket(openingAuctionDuration *types.AuctionDuration) types.Market {
    35  	mkt := types.Market{
    36  		Fees: &types.Fees{
    37  			Factors: &types.FeeFactors{
    38  				LiquidityFee:      num.DecimalFromFloat(0.3),
    39  				InfrastructureFee: num.DecimalFromFloat(0.001),
    40  				MakerFee:          num.DecimalFromFloat(0.004),
    41  			},
    42  		},
    43  		TradableInstrument: &types.TradableInstrument{
    44  			Instrument: &types.Instrument{
    45  				ID:   "Crypto/ETHUSD/Futures/Dec19",
    46  				Code: "CRYPTO:ETHUSD/DEC19",
    47  				Name: "December 2019 ETH vs USD future",
    48  				Metadata: &types.InstrumentMetadata{
    49  					Tags: []string{
    50  						"asset_class:fx/crypto",
    51  						"product:futures",
    52  					},
    53  				},
    54  				Product: &types.InstrumentFuture{
    55  					Future: &types.Future{
    56  						SettlementAsset: "ETH",
    57  						QuoteName:       "USD",
    58  						DataSourceSpecBinding: &datasource.SpecBindingForFuture{
    59  							SettlementDataProperty:     "prices.ETH.value",
    60  							TradingTerminationProperty: "trading.terminated",
    61  						},
    62  					},
    63  				},
    64  			},
    65  			MarginCalculator: &types.MarginCalculator{
    66  				ScalingFactors: &types.ScalingFactors{
    67  					SearchLevel:       num.DecimalFromFloat(1.1),
    68  					InitialMargin:     num.DecimalFromFloat(1.2),
    69  					CollateralRelease: num.DecimalFromFloat(1.4),
    70  				},
    71  			},
    72  			RiskModel: &types.TradableInstrumentSimpleRiskModel{
    73  				SimpleRiskModel: &types.SimpleRiskModel{
    74  					Params: &types.SimpleModelParams{
    75  						FactorLong:           num.DecimalFromFloat(0.15),
    76  						FactorShort:          num.DecimalFromFloat(0.25),
    77  						MaxMoveUp:            num.DecimalFromFloat(100.0),
    78  						MinMoveDown:          num.DecimalFromFloat(100.0),
    79  						ProbabilityOfTrading: num.DecimalFromFloat(0.1),
    80  					},
    81  				},
    82  			},
    83  		},
    84  		OpeningAuction: openingAuctionDuration,
    85  		LiquidityMonitoringParameters: &types.LiquidityMonitoringParameters{
    86  			TargetStakeParameters: &types.TargetStakeParameters{
    87  				TimeWindow:    3600, // seconds = 1h
    88  				ScalingFactor: num.DecimalFromFloat(10),
    89  			},
    90  		},
    91  	}
    92  	return mkt
    93  }
    94  
    95  func createAuctionState() *monitor.AuctionState {
    96  	ad := &types.AuctionDuration{
    97  		Duration: 100,
    98  		Volume:   100,
    99  	}
   100  	mktCfg := getMarket(ad)
   101  	return monitor.NewAuctionState(&mktCfg, time.Now())
   102  }
   103  
   104  func getHash(t *testing.T, as *monitor.AuctionState) []byte {
   105  	t.Helper()
   106  	state := as.GetState()
   107  	pmproto := state.IntoProto()
   108  	bytes, err := proto.Marshal(pmproto)
   109  	require.NoError(t, err)
   110  
   111  	// Check our change flag has been reset
   112  	return crypto.Hash(bytes)
   113  }
   114  
   115  func TestEmpty(t *testing.T) {
   116  	as := createAuctionState()
   117  
   118  	// Get the hash and state for the empty object
   119  	hash1 := getHash(t, as)
   120  	state1 := as.GetState()
   121  
   122  	// Create a new object and restore into it
   123  	ad := &types.AuctionDuration{
   124  		Duration: 100,
   125  		Volume:   100,
   126  	}
   127  	mktCfg := getMarket(ad)
   128  	as2 := monitor.NewAuctionStateFromSnapshot(&mktCfg, state1)
   129  
   130  	// Check the new hash matches the old hash
   131  	assert.Equal(t, hash1, getHash(t, as2))
   132  }
   133  
   134  func TestRestoreTriggerType(t *testing.T) {
   135  	as := createAuctionState()
   136  
   137  	// Perform some updates to the object
   138  	as.StartPriceAuction(time.Now(), &types.AuctionDuration{
   139  		Duration: 200,
   140  		Volume:   200,
   141  	})
   142  
   143  	asNew := monitor.NewAuctionStateFromSnapshot(nil, as.GetState())
   144  	require.Equal(t, as.IsPriceAuction(), asNew.IsPriceAuction())
   145  }
   146  
   147  func TestChangedState(t *testing.T) {
   148  	as := createAuctionState()
   149  
   150  	// Get the hash for the empty object
   151  	original := getHash(t, as)
   152  
   153  	// Perform some updates to the object
   154  	as.StartPriceAuction(time.Now(), &types.AuctionDuration{
   155  		Duration: 200,
   156  		Volume:   200,
   157  	})
   158  
   159  	// Make sure we thinks things have changed
   160  	assert.True(t, as.Changed())
   161  
   162  	auctionStart := getHash(t, as)
   163  	assert.NotEqual(t, original, auctionStart)
   164  
   165  	// extend the auction
   166  	as.ExtendAuction(types.AuctionDuration{Duration: 12, Volume: 12})
   167  	assert.True(t, as.Changed())
   168  
   169  	auctionExtended := getHash(t, as)
   170  	assert.NotEqual(t, auctionStart, auctionExtended)
   171  
   172  	// set Ready to leave
   173  	as.SetReadyToLeave()
   174  	assert.True(t, as.Changed())
   175  
   176  	auctionReady := getHash(t, as)
   177  	assert.NotEqual(t, auctionStart, auctionReady)
   178  
   179  	// end it
   180  	as.Left(context.Background(), time.Now())
   181  	assert.True(t, as.Changed())
   182  
   183  	auctionEnded := getHash(t, as)
   184  	assert.NotEqual(t, auctionStart, auctionEnded)
   185  }
   186  
   187  // TestAuctionTypeChain checks that if an auction is ended, then started again it is logged as a change.
   188  func TestAuctionEndsOpens(t *testing.T) {
   189  	as := createAuctionState()
   190  	now := time.Now()
   191  	// Perform some updates to the object
   192  	as.StartOpeningAuction(now, &types.AuctionDuration{
   193  		Duration: 200,
   194  		Volume:   200,
   195  	})
   196  
   197  	// Get the hash of a started auction
   198  	require.True(t, as.Changed())
   199  	original := getHash(t, as)
   200  
   201  	// Close it down and then start exactly the same auction again
   202  	as.Left(context.Background(), now)
   203  	require.False(t, as.InAuction()) // definitely no auction
   204  
   205  	as.StartOpeningAuction(now, &types.AuctionDuration{
   206  		Duration: 200,
   207  		Volume:   200,
   208  	})
   209  
   210  	require.True(t, as.Changed())
   211  	newAuction := getHash(t, as)
   212  	// change flagged even though hash is exactly the same (which is expected given all the state change that actually occurred)
   213  	assert.Equal(t, original, newAuction)
   214  }