code.vegaprotocol.io/vega@v0.79.0/core/positions/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 positions_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/hex"
    22  	"testing"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/positions"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/crypto"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/libs/proto"
    30  	snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func fillTestPositions(e *positions.SnapshotEngine) {
    37  	orders := []*types.Order{
    38  		{
    39  			Party:     "test_party_1",
    40  			Side:      types.SideBuy,
    41  			Size:      uint64(100),
    42  			Remaining: uint64(100),
    43  			Price:     num.UintZero(),
    44  		},
    45  		{
    46  			Party:     "test_party_2",
    47  			Side:      types.SideBuy,
    48  			Size:      uint64(200),
    49  			Remaining: uint64(200),
    50  			Price:     num.UintZero(),
    51  		},
    52  	}
    53  
    54  	matchingPrice := num.NewUint(10000)
    55  	tradeSize := uint64(15)
    56  	passiveOrder := &types.Order{
    57  		ID:        "buy_order_id",
    58  		Party:     "test_party_3",
    59  		Side:      types.SideBuy,
    60  		Size:      tradeSize,
    61  		Remaining: tradeSize,
    62  		Price:     matchingPrice,
    63  	}
    64  
    65  	aggresiveOrder := &types.Order{
    66  		ID:        "sell_order_id",
    67  		Party:     "test_party_1",
    68  		Side:      types.SideSell,
    69  		Size:      tradeSize,
    70  		Remaining: tradeSize,
    71  		Price:     matchingPrice,
    72  	}
    73  
    74  	orders = append(orders, passiveOrder, aggresiveOrder)
    75  
    76  	for _, order := range orders {
    77  		e.RegisterOrder(context.TODO(), order)
    78  	}
    79  
    80  	trade := types.Trade{
    81  		Type:      types.TradeTypeDefault,
    82  		ID:        "trade_id",
    83  		MarketID:  "market_id",
    84  		Price:     matchingPrice,
    85  		Size:      tradeSize,
    86  		Buyer:     passiveOrder.Party,
    87  		Seller:    aggresiveOrder.Party,
    88  		BuyOrder:  passiveOrder.ID,
    89  		SellOrder: aggresiveOrder.ID,
    90  		Timestamp: time.Now().Unix(),
    91  	}
    92  	e.Update(context.Background(), &trade, passiveOrder, aggresiveOrder)
    93  }
    94  
    95  func TestSnapshotSaveAndLoad(t *testing.T) {
    96  	engine := getTestEngine(t)
    97  	fillTestPositions(engine)
    98  
    99  	keys := engine.Keys()
   100  	require.Equal(t, 1, len(keys))
   101  	require.Equal(t, "test_market", keys[0])
   102  
   103  	s1, _, err := engine.GetState(keys[0])
   104  	require.Nil(t, err)
   105  	s2, _, err := engine.GetState(keys[0])
   106  	require.Nil(t, err)
   107  
   108  	// With no change the states are equal
   109  	require.True(t, bytes.Equal(s1, s2))
   110  
   111  	data, _, err := engine.GetState(keys[0])
   112  	require.Nil(t, err)
   113  
   114  	snap := &snapshot.Payload{}
   115  	err = proto.Unmarshal(data, snap)
   116  	require.Nil(t, err)
   117  
   118  	snapEngine := getTestEngine(t)
   119  	_, err = snapEngine.LoadState(
   120  		context.TODO(),
   121  		types.PayloadFromProto(snap),
   122  	)
   123  	require.Nil(t, err)
   124  
   125  	// Get state again
   126  	s3, _, err := snapEngine.GetState(keys[0])
   127  	require.Nil(t, err)
   128  	require.True(t, bytes.Equal(s1, s3))
   129  	require.Equal(t, len(engine.Positions()), len(snapEngine.Positions()))
   130  	for _, p := range engine.Positions() {
   131  		// find it in the other engine by partyID
   132  		pos, found := snapEngine.GetPositionByPartyID(p.Party())
   133  		require.True(t, found)
   134  		require.Equal(t, p, pos)
   135  	}
   136  }
   137  
   138  func TestSnapshotStateNoChanges(t *testing.T) {
   139  	engine := getTestEngine(t)
   140  	fillTestPositions(engine)
   141  
   142  	keys := engine.Keys()
   143  	s1, _, err := engine.GetState(keys[0])
   144  	require.Nil(t, err)
   145  	s2, _, err := engine.GetState(keys[0])
   146  	require.Nil(t, err)
   147  
   148  	// With no changes we expect the states are equal
   149  	require.True(t, bytes.Equal(s1, s2))
   150  }
   151  
   152  func TestSnapshotStateRegisterOrder(t *testing.T) {
   153  	engine := getTestEngine(t)
   154  	fillTestPositions(engine)
   155  
   156  	keys := engine.Keys()
   157  	s1, _, err := engine.GetState(keys[0])
   158  	require.Nil(t, err)
   159  
   160  	// Add and order and the state should change
   161  	newOrder := &types.Order{
   162  		Party:     "test_party_1",
   163  		Side:      types.SideBuy,
   164  		Size:      uint64(150),
   165  		Remaining: uint64(150),
   166  		Price:     num.UintZero(),
   167  	}
   168  	engine.RegisterOrder(context.TODO(), newOrder)
   169  	s2, _, err := engine.GetState(keys[0])
   170  	require.Nil(t, err)
   171  	require.False(t, bytes.Equal(s1, s2))
   172  }
   173  
   174  func TestSnapshotStateUnregisterOrder(t *testing.T) {
   175  	engine := getTestEngine(t)
   176  	fillTestPositions(engine)
   177  
   178  	keys := engine.Keys()
   179  	s1, _, err := engine.GetState(keys[0])
   180  	require.Nil(t, err)
   181  
   182  	// Add and order and the state should change
   183  	newOrder := &types.Order{
   184  		Party:     "test_party_1",
   185  		Side:      types.SideBuy,
   186  		Size:      uint64(10),
   187  		Remaining: uint64(10),
   188  		Price:     num.UintZero(),
   189  	}
   190  	engine.RegisterOrder(context.TODO(), newOrder)
   191  	s2, _, err := engine.GetState(keys[0])
   192  	require.Nil(t, err)
   193  	require.False(t, bytes.Equal(s1, s2))
   194  }
   195  
   196  func TestSnapshotStateAmendOrder(t *testing.T) {
   197  	engine := getTestEngine(t)
   198  	fillTestPositions(engine)
   199  
   200  	// Add and order and the state should change
   201  	newOrders := []*types.Order{
   202  		{
   203  			Party:     "test_party_1",
   204  			Side:      types.SideBuy,
   205  			Size:      uint64(100),
   206  			Remaining: uint64(100),
   207  			Price:     num.UintZero(),
   208  		},
   209  		{
   210  			Party:     "test_party_1",
   211  			Side:      types.SideBuy,
   212  			Size:      uint64(90),
   213  			Remaining: uint64(90),
   214  			Price:     num.UintZero(),
   215  		},
   216  	}
   217  	engine.RegisterOrder(context.TODO(), newOrders[0])
   218  	keys := engine.Keys()
   219  	s1, _, err := engine.GetState(keys[0])
   220  	require.Nil(t, err)
   221  
   222  	// Amend it
   223  	engine.AmendOrder(context.TODO(), newOrders[0], newOrders[1])
   224  	s2, _, err := engine.GetState(keys[0])
   225  	require.Nil(t, err)
   226  	require.False(t, bytes.Equal(s1, s2))
   227  
   228  	// Then amend it back, state should be the same as originally
   229  	engine.AmendOrder(context.TODO(), newOrders[1], newOrders[0])
   230  	s2, _, err = engine.GetState(keys[0])
   231  	require.Nil(t, err)
   232  	require.True(t, bytes.Equal(s1, s2))
   233  }
   234  
   235  func TestSnapshotStateRemoveDistressed(t *testing.T) {
   236  	engine := getTestEngine(t)
   237  	fillTestPositions(engine)
   238  
   239  	keys := engine.Keys()
   240  	s1, _, err := engine.GetState(keys[0])
   241  	require.Nil(t, err)
   242  
   243  	engine.RemoveDistressed(engine.Positions())
   244  	s2, _, err := engine.GetState(keys[0])
   245  	require.Nil(t, err)
   246  	require.False(t, bytes.Equal(s1, s2))
   247  }
   248  
   249  func TestSnapshotStaeUpdateMarkPrice(t *testing.T) {
   250  	engine := getTestEngine(t)
   251  	fillTestPositions(engine)
   252  
   253  	keys := engine.Keys()
   254  	s1, _, err := engine.GetState(keys[0])
   255  	require.Nil(t, err)
   256  
   257  	engine.UpdateMarkPrice(num.NewUint(12))
   258  	s2, _, err := engine.GetState(keys[0])
   259  	require.Nil(t, err)
   260  	require.False(t, bytes.Equal(s1, s2))
   261  }
   262  
   263  func TestSnapshotHashNoPositions(t *testing.T) {
   264  	engine := getTestEngine(t)
   265  
   266  	keys := engine.Keys()
   267  	s1, _, err := engine.GetState(keys[0])
   268  	require.Nil(t, err)
   269  	require.Equal(t, "278f2eff5adc1ea5b8365bd04c6e534ef64ca43df737c22ee61db46a8dac5870", hex.EncodeToString(crypto.Hash(s1)))
   270  }
   271  
   272  func TestStopSnapshotTaking(t *testing.T) {
   273  	engine := getTestEngine(t)
   274  	keys := engine.Keys()
   275  
   276  	// signal to kill the engine's snapshots
   277  	engine.StopSnapshots()
   278  
   279  	s, _, err := engine.GetState(keys[0])
   280  	assert.NoError(t, err)
   281  	assert.Nil(t, s)
   282  	assert.True(t, engine.Stopped())
   283  }
   284  
   285  func TestSnapshotClosedPositionStillSerializeStats(t *testing.T) {
   286  	engine := getTestEngine(t)
   287  	// fillTestPositions(engine)
   288  
   289  	keys := engine.Keys()
   290  	s1, _, err := engine.GetState(keys[0])
   291  	require.Nil(t, err)
   292  
   293  	// first load two orders
   294  
   295  	buyOrder := &types.Order{
   296  		Party:     "test_party_1",
   297  		Side:      types.SideBuy,
   298  		Size:      uint64(150),
   299  		Remaining: uint64(150),
   300  		Price:     num.UintZero(),
   301  	}
   302  	engine.RegisterOrder(context.TODO(), buyOrder)
   303  
   304  	sellOrder := &types.Order{
   305  		Party:     "test_party_2",
   306  		Side:      types.SideSell,
   307  		Size:      uint64(150),
   308  		Remaining: uint64(150),
   309  		Price:     num.UintZero(),
   310  	}
   311  	engine.RegisterOrder(context.TODO(), sellOrder)
   312  
   313  	// then get them to update the positions
   314  
   315  	trade := &types.Trade{
   316  		Size:   150,
   317  		Buyer:  "test_party_1",
   318  		Seller: "test_party_2",
   319  	}
   320  
   321  	engine.Update(context.TODO(), trade, buyOrder, sellOrder)
   322  
   323  	// now we close the positions
   324  	// just swap the parties on the orders
   325  	// and trades
   326  	buyOrder.Party, sellOrder.Party = sellOrder.Party, buyOrder.Party
   327  	trade.Buyer, trade.Seller = trade.Seller, trade.Buyer
   328  
   329  	engine.RegisterOrder(context.TODO(), buyOrder)
   330  	engine.RegisterOrder(context.TODO(), sellOrder)
   331  	engine.Update(context.TODO(), trade, buyOrder, sellOrder)
   332  
   333  	// should get 2 closed positions
   334  	assert.Len(t, engine.GetClosedPositions(), 2)
   335  
   336  	s2, _, err := engine.GetState(keys[0])
   337  	require.Nil(t, err)
   338  	require.False(t, bytes.Equal(s1, s2))
   339  
   340  	payload := &snapshot.Payload{}
   341  	assert.NoError(t, proto.Unmarshal(s2, payload))
   342  
   343  	marketPositions := payload.Data.(*snapshot.Payload_MarketPositions)
   344  	assert.NotNil(t, marketPositions)
   345  	// assert the records are saved
   346  	assert.Len(t, marketPositions.MarketPositions.PartiesRecords, 2)
   347  	// while yet, there's no positions anymores.
   348  	assert.Len(t, marketPositions.MarketPositions.Positions, 0)
   349  }