code.vegaprotocol.io/vega@v0.79.0/core/execution/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 execution_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"code.vegaprotocol.io/vega/core/collateral"
    28  	"code.vegaprotocol.io/vega/core/datasource"
    29  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    30  	"code.vegaprotocol.io/vega/core/datasource/external/signedoracle"
    31  	"code.vegaprotocol.io/vega/core/datasource/spec"
    32  	"code.vegaprotocol.io/vega/core/epochtime"
    33  	"code.vegaprotocol.io/vega/core/execution"
    34  	"code.vegaprotocol.io/vega/core/execution/common"
    35  	"code.vegaprotocol.io/vega/core/execution/common/mocks"
    36  	fmock "code.vegaprotocol.io/vega/core/fee/mocks"
    37  	"code.vegaprotocol.io/vega/core/integration/stubs"
    38  	snp "code.vegaprotocol.io/vega/core/snapshot"
    39  	"code.vegaprotocol.io/vega/core/stats"
    40  	"code.vegaprotocol.io/vega/core/types"
    41  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    42  	"code.vegaprotocol.io/vega/libs/crypto"
    43  	"code.vegaprotocol.io/vega/libs/num"
    44  	"code.vegaprotocol.io/vega/libs/proto"
    45  	vgtest "code.vegaprotocol.io/vega/libs/test"
    46  	"code.vegaprotocol.io/vega/logging"
    47  	"code.vegaprotocol.io/vega/paths"
    48  	"code.vegaprotocol.io/vega/protos/vega"
    49  	datapb "code.vegaprotocol.io/vega/protos/vega/data/v1"
    50  	snapshot "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    51  
    52  	"github.com/golang/mock/gomock"
    53  	"github.com/stretchr/testify/assert"
    54  	"github.com/stretchr/testify/require"
    55  )
    56  
    57  type snapshotTestData struct {
    58  	engine           *execution.Engine
    59  	oracleEngine     *spec.Engine
    60  	snapshotEngine   *snp.Engine
    61  	timeService      *stubs.TimeStub
    62  	collateralEngine *collateral.Engine
    63  }
    64  
    65  type stubIDGen struct {
    66  	calls int
    67  }
    68  
    69  // TestSnapshotOraclesTerminatingMarketFromSnapshot tests that market loaded from snapshot can be terminated with its oracle.
    70  func TestSnapshotOraclesTerminatingMarketFromSnapshot(t *testing.T) {
    71  	now := time.Now()
    72  	exec := getEngine(t, paths.New(t.TempDir()), now)
    73  	pubKey := &dstypes.SignerPubKey{
    74  		PubKey: &dstypes.PubKey{
    75  			Key: "0xDEADBEEF",
    76  		},
    77  	}
    78  	mkt := newMarket("MarketID", pubKey)
    79  	err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now())
    80  	require.NoError(t, err)
    81  
    82  	marketState, _, _ := exec.engine.GetState("")
    83  
    84  	exec2 := getEngine(t, paths.New(t.TempDir()), now)
    85  	marketSnap := &snapshot.Payload{}
    86  	proto.Unmarshal(marketState, marketSnap)
    87  
    88  	_, _ = exec2.engine.LoadState(context.Background(), types.PayloadFromProto(marketSnap))
    89  
    90  	// restore collateral
    91  	accountsState, _, _ := exec.collateralEngine.GetState("accounts")
    92  	accountsSnap := &snapshot.Payload{}
    93  	proto.Unmarshal(accountsState, accountsSnap)
    94  
    95  	_, _ = exec2.collateralEngine.LoadState(context.Background(), types.PayloadFromProto(accountsSnap))
    96  
    97  	state2, _, _ := exec2.engine.GetState("")
    98  
    99  	err = exec.engine.StartOpeningAuction(context.Background(), mkt.ID)
   100  	require.NoError(t, err)
   101  	mktState, err := exec.engine.GetMarketState("MarketID")
   102  	require.NoError(t, err)
   103  	require.Equal(t, types.MarketStateActive, mktState)
   104  
   105  	err = exec2.engine.StartOpeningAuction(context.Background(), mkt.ID)
   106  	require.NoError(t, err)
   107  	mktState, err = exec2.engine.GetMarketState("MarketID")
   108  	require.NoError(t, err)
   109  	require.Equal(t, types.MarketStateActive, mktState)
   110  
   111  	pubKeys := []*dstypes.Signer{
   112  		dstypes.CreateSignerFromString(pubKey.PubKey.Key, dstypes.SignerTypePubKey),
   113  	}
   114  
   115  	exec.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   116  		Signers: pubKeys,
   117  		Data:    map[string]string{"trading.terminated": "true"},
   118  	})
   119  
   120  	exec2.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   121  		Signers: pubKeys,
   122  		Data:    map[string]string{"trading.terminated": "true"},
   123  	})
   124  
   125  	marketState1, _ := exec.engine.GetMarketState("MarketID")
   126  	marketState2, _ := exec2.engine.GetMarketState("MarketID")
   127  
   128  	require.Equal(t, marketState1, marketState2)
   129  	require.Equal(t, types.MarketStateTradingTerminated, marketState1)
   130  	require.Equal(t, types.MarketStateTradingTerminated, marketState2)
   131  
   132  	exec.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   133  		Signers: pubKeys,
   134  		Data:    map[string]string{"prices.ETH.value": "100"},
   135  	})
   136  
   137  	exec2.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   138  		Signers: pubKeys,
   139  		Data:    map[string]string{"prices.ETH.value": "100"},
   140  	})
   141  
   142  	marketState1, _ = exec.engine.GetMarketState("MarketID")
   143  	marketState2, _ = exec2.engine.GetMarketState("MarketID")
   144  	require.Equal(t, marketState1, marketState2)
   145  	require.Equal(t, types.MarketStateSettled, marketState1)
   146  	require.Equal(t, types.MarketStateSettled, marketState2)
   147  
   148  	require.True(t, bytes.Equal(marketState, state2))
   149  }
   150  
   151  // TestSnapshotOraclesTerminatingMarketSettleAfterSnapshot tests that market loaded from snapshot can be terminated with its oracle.
   152  // the settlement data will be sent before the snapshot is taken, to ensure settlement data is restored correctly.
   153  func TestSnapshotOraclesTerminatingMarketSettleAfterSnapshot(t *testing.T) {
   154  	now := time.Now()
   155  	exec := getEngineWithParties(t, now, num.NewUint(1000000000), "lp", "p1", "p2", "p3", "p4")
   156  	pubKey := &dstypes.SignerPubKey{
   157  		PubKey: &dstypes.PubKey{
   158  			Key: "0xDEADBEEF",
   159  		},
   160  	}
   161  
   162  	mkt := newMarketWithAuctionDuration("MarketID", pubKey, &types.AuctionDuration{Duration: 1})
   163  	err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now())
   164  	require.NoError(t, err)
   165  
   166  	err = exec.engine.StartOpeningAuction(context.Background(), mkt.ID)
   167  	require.NoError(t, err)
   168  	mktState, err := exec.engine.GetMarketState("MarketID")
   169  	require.NoError(t, err)
   170  	require.Equal(t, types.MarketStatePending, mktState)
   171  
   172  	md, err := exec.engine.GetMarketData(mkt.ID)
   173  	require.NoError(t, err)
   174  	require.Equal(t, types.MarketTradingModeOpeningAuction, md.MarketTradingMode)
   175  
   176  	idgen := &stubIDGen{}
   177  	// now let's submit some orders and get market to trade continuously
   178  	lpSubmission := &types.LiquidityProvisionSubmission{
   179  		MarketID:         mkt.ID,
   180  		CommitmentAmount: num.NewUint(1000000),
   181  		Fee:              num.DecimalFromFloat(0.01),
   182  		Reference:        "lp1",
   183  	}
   184  	// submit LP
   185  	vgctx := vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("0deadbeef")))
   186  	_ = exec.engine.SubmitLiquidityProvision(vgctx, lpSubmission, "lp", idgen.NextID())
   187  	// uncrossing orders
   188  	os1 := &types.OrderSubmission{
   189  		MarketID:    mkt.ID,
   190  		Price:       num.NewUint(99),
   191  		Size:        1,
   192  		Side:        types.SideBuy,
   193  		TimeInForce: types.OrderTimeInForceGTC,
   194  		Type:        types.OrderTypeLimit,
   195  		Reference:   "o1",
   196  	}
   197  	os2 := &types.OrderSubmission{
   198  		MarketID:    mkt.ID,
   199  		Price:       num.NewUint(99),
   200  		Size:        1,
   201  		Side:        types.SideSell,
   202  		TimeInForce: types.OrderTimeInForceGTC,
   203  		Type:        types.OrderTypeLimit,
   204  		Reference:   "o2",
   205  	}
   206  	_, _ = exec.engine.SubmitOrder(vgctx, os1, "p1", idgen, "o1p1")
   207  	_, _ = exec.engine.SubmitOrder(vgctx, os2, "p2", idgen, "o2p2")
   208  	// have some volume on the book
   209  	os1 = &types.OrderSubmission{
   210  		MarketID:    mkt.ID,
   211  		Price:       num.NewUint(85),
   212  		Size:        1,
   213  		Side:        types.SideBuy,
   214  		TimeInForce: types.OrderTimeInForceGTC,
   215  		Type:        types.OrderTypeLimit,
   216  		Reference:   "o3",
   217  	}
   218  	os2 = &types.OrderSubmission{
   219  		MarketID:    mkt.ID,
   220  		Price:       num.NewUint(110),
   221  		Size:        1,
   222  		Side:        types.SideSell,
   223  		TimeInForce: types.OrderTimeInForceGTC,
   224  		Type:        types.OrderTypeLimit,
   225  		Reference:   "o4",
   226  	}
   227  	_, _ = exec.engine.SubmitOrder(vgctx, os1, "p3", idgen, "o3p3")
   228  	_, _ = exec.engine.SubmitOrder(vgctx, os2, "p4", idgen, "o4p4")
   229  
   230  	// OK, we now have stuff on the book, so we should be able to leave opening auction
   231  	now = now.Add(60 * time.Second) // move ahead 1 minute
   232  	// We probably need to add a hash to this context
   233  	vgctx = vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("1deadbeef")))
   234  	exec.engine.OnTick(vgctx, now)
   235  	pubKeys := []*dstypes.Signer{
   236  		dstypes.CreateSignerFromString(pubKey.PubKey.Key, dstypes.SignerTypePubKey),
   237  	}
   238  
   239  	// provide settlement data for first market
   240  	vgctx = vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("2deadbeef")))
   241  	exec.oracleEngine.BroadcastData(vgctx, dstypes.Data{
   242  		Signers: pubKeys,
   243  		Data:    map[string]string{"prices.ETH.value": "100"},
   244  	})
   245  	// then create snapshot of market
   246  	state, _, _ := exec.engine.GetState("")
   247  
   248  	exec2 := getEngine(t, paths.New(t.TempDir()), now)
   249  	snap := &snapshot.Payload{}
   250  	proto.Unmarshal(state, snap)
   251  	vgctx = vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("3deadbeef")))
   252  	_, _ = exec2.engine.LoadState(vgctx, types.PayloadFromProto(snap))
   253  
   254  	state2, _, _ := exec2.engine.GetState("")
   255  	// the states should match
   256  	require.True(t, bytes.Equal(state, state2))
   257  
   258  	// restore collateral
   259  	accountsState, _, _ := exec.collateralEngine.GetState("accounts")
   260  	accountsSnap := &snapshot.Payload{}
   261  	proto.Unmarshal(accountsState, accountsSnap)
   262  
   263  	_, _ = exec2.collateralEngine.LoadState(context.Background(), types.PayloadFromProto(accountsSnap))
   264  
   265  	vgctx = vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("3deadbeef")))
   266  	exec.oracleEngine.BroadcastData(vgctx, dstypes.Data{
   267  		Signers: pubKeys,
   268  		Data:    map[string]string{"trading.terminated": "true"},
   269  	})
   270  
   271  	exec2.oracleEngine.BroadcastData(vgctx, dstypes.Data{
   272  		Signers: pubKeys,
   273  		Data:    map[string]string{"trading.terminated": "true"},
   274  	})
   275  
   276  	marketState1, _ := exec.engine.GetMarketState("MarketID")
   277  	marketState2, _ := exec2.engine.GetMarketState("MarketID")
   278  
   279  	// markets should both be settled
   280  	require.Equal(t, marketState1, marketState2)
   281  	require.Equal(t, types.MarketStateSettled, marketState1)
   282  	require.Equal(t, types.MarketStateSettled, marketState2)
   283  }
   284  
   285  // TestSnapshotOraclesTerminatingMarketFromSnapshotAfterSettlementData sets up a market that gets the settlement data first.
   286  // Then a snapshot is taken and another node is restored from this snapshot. Finally trading termination data is received and both markets
   287  // are expected to get settled.
   288  func TestSnapshotOraclesTerminatingMarketFromSnapshotAfterSettlementData(t *testing.T) {
   289  	pubKeys := []*dstypes.Signer{
   290  		dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey),
   291  	}
   292  
   293  	now := time.Now()
   294  	exec := getEngine(t, paths.New(t.TempDir()), now)
   295  	mkt := newMarket("MarketID", pubKeys[0].Signer.(*dstypes.SignerPubKey))
   296  	err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now())
   297  	require.NoError(t, err)
   298  
   299  	err = exec.engine.StartOpeningAuction(context.Background(), mkt.ID)
   300  	require.NoError(t, err)
   301  	mktState, err := exec.engine.GetMarketState("MarketID")
   302  	require.NoError(t, err)
   303  	require.Equal(t, types.MarketStateActive, mktState)
   304  
   305  	// set up market to get to continuous trading
   306  
   307  	// settlement data arrives first
   308  	exec.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   309  		Signers: pubKeys,
   310  		Data:    map[string]string{"prices.ETH.value": "100"},
   311  	})
   312  
   313  	// take a snapshot
   314  	state, _, _ := exec.engine.GetState("")
   315  
   316  	// load from the snapshot
   317  	exec2 := getEngine(t, paths.New(t.TempDir()), now)
   318  	snap := &snapshot.Payload{}
   319  	proto.Unmarshal(state, snap)
   320  	_, _ = exec2.engine.LoadState(context.Background(), types.PayloadFromProto(snap))
   321  
   322  	// take a snapshot on the loaded engine
   323  	state2, _, _ := exec2.engine.GetState("")
   324  	require.True(t, bytes.Equal(state, state2))
   325  
   326  	// restore collateral
   327  	accountsState, _, _ := exec.collateralEngine.GetState("accounts")
   328  	accountsSnap := &snapshot.Payload{}
   329  	proto.Unmarshal(accountsState, accountsSnap)
   330  
   331  	_, _ = exec2.collateralEngine.LoadState(context.Background(), types.PayloadFromProto(accountsSnap))
   332  
   333  	// terminate the market to lead to settlement
   334  	exec.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   335  		Signers: pubKeys,
   336  		Data:    map[string]string{"trading.terminated": "true"},
   337  	})
   338  
   339  	exec2.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   340  		Signers: pubKeys,
   341  		Data:    map[string]string{"trading.terminated": "true"},
   342  	})
   343  
   344  	// take snapshot for both engines, and verify they're both settled
   345  	marketState1, _ := exec.engine.GetMarketState("MarketID")
   346  	marketState2, _ := exec2.engine.GetMarketState("MarketID")
   347  	require.Equal(t, marketState1, marketState2)
   348  	require.Equal(t, types.MarketStateSettled, marketState1)
   349  	require.Equal(t, types.MarketStateSettled, marketState2)
   350  }
   351  
   352  // TestLoadTerminatedMarketFromSnapshot terminates markets, loads them using the snapshot engine and then settles them successfully.
   353  func TestLoadTerminatedMarketFromSnapshot(t *testing.T) {
   354  	ctx := vgtest.VegaContext("chainid", 100)
   355  
   356  	now := time.Now()
   357  	vegaPath := paths.New(t.TempDir())
   358  	executionEngine1 := getEngine(t, vegaPath, now)
   359  	snapshotEngine1CloseFn := vgtest.OnlyOnce(executionEngine1.snapshotEngine.Close)
   360  	defer snapshotEngine1CloseFn()
   361  
   362  	require.NoError(t, executionEngine1.snapshotEngine.Start(ctx))
   363  
   364  	pubKeys := []*dstypes.Signer{
   365  		dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey),
   366  		dstypes.CreateSignerFromString("0xDEADBEFF", dstypes.SignerTypePubKey),
   367  		dstypes.CreateSignerFromString("0xDEADBFFF", dstypes.SignerTypePubKey),
   368  	}
   369  	marketIDs := []string{"market1", "market2", "market3"}
   370  
   371  	// submit and terminate all markets
   372  	for i := 0; i < 3; i++ {
   373  		mkt := newMarket(marketIDs[i], pubKeys[i].Signer.(*dstypes.SignerPubKey))
   374  		err := executionEngine1.engine.SubmitMarket(ctx, mkt, "", time.Now())
   375  		require.NoError(t, err)
   376  
   377  		// verify markets are terminated
   378  		marketState, err := executionEngine1.engine.GetMarketState(marketIDs[i])
   379  		require.NoError(t, err)
   380  		require.Equal(t, types.MarketStateProposed, marketState)
   381  
   382  		err = executionEngine1.engine.StartOpeningAuction(context.Background(), mkt.ID)
   383  		require.NoError(t, err)
   384  		marketState, err = executionEngine1.engine.GetMarketState(marketIDs[i])
   385  		require.NoError(t, err)
   386  		require.Equal(t, marketState, types.MarketStateActive)
   387  
   388  		// terminate all markets
   389  		require.NoError(t, executionEngine1.oracleEngine.BroadcastData(ctx, dstypes.Data{
   390  			Signers: []*dstypes.Signer{pubKeys[i]},
   391  			Data:    map[string]string{"trading.terminated": "true"},
   392  		}))
   393  
   394  		marketState, err = executionEngine1.engine.GetMarketState(marketIDs[i])
   395  		require.NoError(t, err)
   396  		require.Equal(t, types.MarketStateTradingTerminated, marketState)
   397  	}
   398  
   399  	// we now have 3 terminated markets in the execution engine
   400  	// let's take a snapshot
   401  	hash1, err := executionEngine1.snapshotEngine.SnapshotNow(ctx)
   402  	require.NoError(t, err)
   403  
   404  	executionEngine1.timeService.SetTime(now.Add(2 * time.Second))
   405  
   406  	for i := 0; i < 3; i++ {
   407  		require.NoError(t, executionEngine1.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   408  			Signers: []*dstypes.Signer{pubKeys[i]},
   409  			Data:    map[string]string{"prices.ETH.value": "100"},
   410  		}))
   411  
   412  		marketState1, err := executionEngine1.engine.GetMarketState(marketIDs[i])
   413  		require.NoError(t, err)
   414  		require.Equal(t, types.MarketStateSettled, marketState1)
   415  	}
   416  
   417  	state1 := map[string][]byte{}
   418  	for _, key := range executionEngine1.engine.Keys() {
   419  		state, additionalProvider, err := executionEngine1.engine.GetState(key)
   420  		require.NoError(t, err)
   421  		assert.Empty(t, additionalProvider)
   422  		state1[key] = state
   423  	}
   424  
   425  	snapshotEngine1CloseFn()
   426  
   427  	// now let's start from this snapshot
   428  	executionEngine2 := getEngine(t, vegaPath, now)
   429  	defer executionEngine2.snapshotEngine.Close()
   430  
   431  	// This triggers the state restoration from the local snapshot.
   432  	require.NoError(t, executionEngine2.snapshotEngine.Start(context.Background()))
   433  
   434  	// Comparing the hash after restoration, to ensure it produces the same result.
   435  	hash2, _, _ := executionEngine2.snapshotEngine.Info()
   436  	require.Equal(t, hash1, hash2)
   437  
   438  	// progress time to trigger any side effect on time ticks
   439  	executionEngine2.timeService.SetTime(now.Add(2 * time.Second))
   440  
   441  	// settle the markets
   442  	for i := 0; i < 3; i++ {
   443  		require.NoError(t, executionEngine2.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   444  			Signers: []*dstypes.Signer{pubKeys[i]},
   445  			Data:    map[string]string{"prices.ETH.value": "100"},
   446  		}))
   447  
   448  		marketState2, err := executionEngine2.engine.GetMarketState(marketIDs[i])
   449  		require.NoError(t, err)
   450  		require.Equal(t, types.MarketStateSettled, marketState2)
   451  	}
   452  
   453  	state2 := map[string][]byte{}
   454  	for _, key := range executionEngine2.engine.Keys() {
   455  		state, additionalProvider, err := executionEngine2.engine.GetState(key)
   456  		require.NoError(t, err)
   457  		assert.Empty(t, additionalProvider)
   458  		state2[key] = state
   459  	}
   460  
   461  	for key := range state1 {
   462  		assert.Equalf(t, state1[key], state2[key], "Key %q does not have the same data", key)
   463  	}
   464  }
   465  
   466  func newMarket(ID string, pubKey *dstypes.SignerPubKey) *types.Market {
   467  	return newMarketWithAuctionDuration(ID, pubKey, nil)
   468  }
   469  
   470  func newMarketWithAuctionDuration(ID string, pubKey *dstypes.SignerPubKey, auctionDuration *types.AuctionDuration) *types.Market {
   471  	return &types.Market{
   472  		ID: ID, // ID will be generated
   473  		PriceMonitoringSettings: &types.PriceMonitoringSettings{
   474  			Parameters: &types.PriceMonitoringParameters{
   475  				Triggers: []*types.PriceMonitoringTrigger{
   476  					{
   477  						Horizon:          1000,
   478  						HorizonDec:       num.DecimalFromFloat(1000.0),
   479  						Probability:      num.DecimalFromFloat(0.3),
   480  						AuctionExtension: 10000,
   481  					},
   482  				},
   483  			},
   484  		},
   485  		LiquidityMonitoringParameters: &types.LiquidityMonitoringParameters{
   486  			TargetStakeParameters: &types.TargetStakeParameters{
   487  				TimeWindow:    100,
   488  				ScalingFactor: num.DecimalFromFloat(1.0),
   489  			},
   490  		},
   491  		Fees: &types.Fees{
   492  			Factors: &types.FeeFactors{
   493  				MakerFee:          num.DecimalFromFloat(0.1),
   494  				InfrastructureFee: num.DecimalFromFloat(0.1),
   495  				LiquidityFee:      num.DecimalFromFloat(0.1),
   496  			},
   497  			LiquidityFeeSettings: &types.LiquidityFeeSettings{
   498  				Method: vega.LiquidityFeeSettings_METHOD_MARGINAL_COST,
   499  			},
   500  		},
   501  		TradableInstrument: &types.TradableInstrument{
   502  			MarginCalculator: &types.MarginCalculator{
   503  				ScalingFactors: &types.ScalingFactors{
   504  					SearchLevel:       num.DecimalFromFloat(1.2),
   505  					InitialMargin:     num.DecimalFromFloat(1.3),
   506  					CollateralRelease: num.DecimalFromFloat(1.4),
   507  				},
   508  			},
   509  			Instrument: &types.Instrument{
   510  				ID:   "Crypto/ETHUSD/Futures/Dec19",
   511  				Code: "FX:ETHUSD/DEC19",
   512  				Name: "December 2019 ETH vs USD future",
   513  				Metadata: &types.InstrumentMetadata{
   514  					Tags: []string{
   515  						"asset_class:fx/crypto",
   516  						"product:futures",
   517  					},
   518  				},
   519  				Product: &types.InstrumentFuture{
   520  					Future: &types.Future{
   521  						SettlementAsset: "Ethereum/Ether",
   522  						DataSourceSpecForSettlementData: &datasource.Spec{
   523  							ID: hex.EncodeToString(crypto.Hash([]byte(ID + "price"))),
   524  							Data: datasource.NewDefinition(
   525  								datasource.ContentTypeOracle,
   526  							).SetOracleConfig(
   527  								&signedoracle.SpecConfiguration{
   528  									Signers: []*dstypes.Signer{dstypes.CreateSignerFromString(pubKey.PubKey.Key, dstypes.SignerTypePubKey)},
   529  									Filters: []*dstypes.SpecFilter{
   530  										{
   531  											Key: &dstypes.SpecPropertyKey{
   532  												Name: "prices.ETH.value",
   533  												Type: datapb.PropertyKey_TYPE_INTEGER,
   534  											},
   535  											Conditions: []*dstypes.SpecCondition{},
   536  										},
   537  									},
   538  								},
   539  							),
   540  						},
   541  						DataSourceSpecForTradingTermination: &datasource.Spec{
   542  							ID: hex.EncodeToString(crypto.Hash([]byte(ID + "tt"))),
   543  							Data: datasource.NewDefinition(
   544  								datasource.ContentTypeOracle,
   545  							).SetOracleConfig(
   546  								&signedoracle.SpecConfiguration{
   547  									Signers: []*dstypes.Signer{dstypes.CreateSignerFromString(pubKey.PubKey.Key, dstypes.SignerTypePubKey)},
   548  									Filters: []*dstypes.SpecFilter{
   549  										{
   550  											Key: &dstypes.SpecPropertyKey{
   551  												Name: "trading.terminated",
   552  												Type: datapb.PropertyKey_TYPE_BOOLEAN,
   553  											},
   554  											Conditions: []*dstypes.SpecCondition{},
   555  										},
   556  									},
   557  								},
   558  							),
   559  						},
   560  						DataSourceSpecBinding: &datasource.SpecBindingForFuture{
   561  							SettlementDataProperty:     "prices.ETH.value",
   562  							TradingTerminationProperty: "trading.terminated",
   563  						},
   564  					},
   565  				},
   566  			},
   567  			RiskModel: &types.TradableInstrumentLogNormalRiskModel{
   568  				LogNormalRiskModel: &types.LogNormalRiskModel{
   569  					RiskAversionParameter: num.DecimalFromFloat(0.01),
   570  					Tau:                   num.DecimalFromFloat(1.0 / 365.25 / 24),
   571  					Params: &types.LogNormalModelParams{
   572  						Mu:    num.DecimalZero(),
   573  						R:     num.DecimalFromFloat(0.016),
   574  						Sigma: num.DecimalFromFloat(0.09),
   575  					},
   576  				},
   577  			},
   578  		},
   579  		LiquiditySLAParams: &types.LiquiditySLAParams{
   580  			PriceRange:                  num.DecimalFromFloat(0.95),
   581  			CommitmentMinTimeFraction:   num.NewDecimalFromFloat(0.5),
   582  			PerformanceHysteresisEpochs: 4,
   583  			SlaCompetitionFactor:        num.NewDecimalFromFloat(0.5),
   584  		},
   585  		State: types.MarketStateActive,
   586  		MarkPriceConfiguration: &types.CompositePriceConfiguration{
   587  			DecayWeight:              num.DecimalZero(),
   588  			DecayPower:               num.DecimalZero(),
   589  			CashAmount:               num.UintZero(),
   590  			SourceWeights:            []num.Decimal{num.DecimalFromFloat(0.1), num.DecimalFromFloat(0.2), num.DecimalFromFloat(0.3), num.DecimalFromFloat(0.4)},
   591  			SourceStalenessTolerance: []time.Duration{0, 0, 0, 0},
   592  			CompositePriceType:       types.CompositePriceTypeByLastTrade,
   593  		},
   594  		TickSize:       num.UintOne(),
   595  		OpeningAuction: auctionDuration,
   596  	}
   597  }
   598  
   599  func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestData {
   600  	t.Helper()
   601  	cfg := execution.NewDefaultConfig()
   602  	log := logging.NewTestLogger()
   603  	broker := stubs.NewBrokerStub()
   604  	timeService := stubs.NewTimeStub()
   605  	timeService.SetTime(now)
   606  	collateralEngine := collateral.New(log, collateral.NewDefaultConfig(), timeService, broker)
   607  	oracleEngine := spec.NewEngine(log, spec.NewDefaultConfig(), timeService, broker)
   608  
   609  	epochEngine := epochtime.NewService(log, epochtime.NewDefaultConfig(), broker)
   610  	ctrl := gomock.NewController(t)
   611  	teams := mocks.NewMockTeams(ctrl)
   612  	bc := mocks.NewMockAccountBalanceChecker(ctrl)
   613  	marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine)
   614  	epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore)
   615  
   616  	ethAsset := types.Asset{
   617  		ID: "Ethereum/Ether",
   618  		Details: &types.AssetDetails{
   619  			Name:    "Ethereum/Ether",
   620  			Symbol:  "Ethereum/Ether",
   621  			Quantum: num.DecimalFromInt64(1),
   622  		},
   623  	}
   624  	require.NoError(t, collateralEngine.EnableAsset(context.Background(), ethAsset))
   625  	referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl)
   626  	volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl)
   627  	volumeRebate := fmock.NewMockVolumeRebateService(ctrl)
   628  	referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   629  	referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   630  	volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   631  	referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
   632  	banking := mocks.NewMockBanking(ctrl)
   633  	parties := mocks.NewMockParties(ctrl)
   634  	delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl)
   635  	delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes()
   636  	eng := execution.NewEngine(
   637  		log,
   638  		cfg,
   639  		timeService,
   640  		collateralEngine,
   641  		oracleEngine,
   642  		broker,
   643  		stubs.NewStateVar(),
   644  		marketActivityTracker,
   645  		stubs.NewAssetStub(),
   646  		referralDiscountReward,
   647  		volumeDiscount,
   648  		volumeRebate,
   649  		banking,
   650  		parties,
   651  		delayTarget,
   652  	)
   653  
   654  	statsData := stats.New(log, stats.NewDefaultConfig())
   655  	config := snp.DefaultConfig()
   656  	snapshotEngine, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain)
   657  	require.NoError(t, err)
   658  	snapshotEngine.AddProviders(eng)
   659  	snapshotEngine.AddProviders(collateralEngine)
   660  
   661  	return &snapshotTestData{
   662  		engine:           eng,
   663  		oracleEngine:     oracleEngine,
   664  		snapshotEngine:   snapshotEngine,
   665  		timeService:      timeService,
   666  		collateralEngine: collateralEngine,
   667  	}
   668  }
   669  
   670  func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, parties ...string) *snapshotTestData {
   671  	t.Helper()
   672  	// ctrl := gomock.NewController(t)
   673  	cfg := execution.NewDefaultConfig()
   674  	log := logging.NewTestLogger()
   675  	broker := stubs.NewBrokerStub()
   676  	timeService := stubs.NewTimeStub()
   677  	timeService.SetTime(now)
   678  	collateralEngine := collateral.New(log, collateral.NewDefaultConfig(), timeService, broker)
   679  	oracleEngine := spec.NewEngine(log, spec.NewDefaultConfig(), timeService, broker)
   680  
   681  	epochEngine := epochtime.NewService(log, epochtime.NewDefaultConfig(), broker)
   682  	ctrl := gomock.NewController(t)
   683  	teams := mocks.NewMockTeams(ctrl)
   684  	bc := mocks.NewMockAccountBalanceChecker(ctrl)
   685  	marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine)
   686  	epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore)
   687  
   688  	ethAsset := types.Asset{
   689  		ID: "Ethereum/Ether",
   690  		Details: &types.AssetDetails{
   691  			Name:    "Ethereum/Ether",
   692  			Symbol:  "Ethereum/Ether",
   693  			Quantum: num.DecimalFromInt64(1),
   694  		},
   695  	}
   696  	collateralEngine.EnableAsset(context.Background(), ethAsset)
   697  	for _, p := range parties {
   698  		_, _ = collateralEngine.Deposit(context.Background(), p, ethAsset.ID, balance.Clone())
   699  	}
   700  	referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl)
   701  	volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl)
   702  	volumeRebate := fmock.NewMockVolumeRebateService(ctrl)
   703  
   704  	referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   705  	referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   706  	volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   707  	referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
   708  	banking := mocks.NewMockBanking(ctrl)
   709  	partiesMock := mocks.NewMockParties(ctrl)
   710  	delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl)
   711  	delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes()
   712  	eng := execution.NewEngine(
   713  		log,
   714  		cfg,
   715  		timeService,
   716  		collateralEngine,
   717  		oracleEngine,
   718  		broker,
   719  		stubs.NewStateVar(),
   720  		marketActivityTracker,
   721  		stubs.NewAssetStub(),
   722  		referralDiscountReward,
   723  		volumeDiscount,
   724  		volumeRebate,
   725  		banking,
   726  		partiesMock,
   727  		delayTarget,
   728  	)
   729  
   730  	statsData := stats.New(log, stats.NewDefaultConfig())
   731  	config := snp.DefaultConfig()
   732  	config.Storage = "memory"
   733  	snapshotEngine, _ := snp.NewEngine(&paths.DefaultPaths{}, config, log, timeService, statsData.Blockchain)
   734  	snapshotEngine.AddProviders(eng)
   735  
   736  	return &snapshotTestData{
   737  		engine:           eng,
   738  		oracleEngine:     oracleEngine,
   739  		snapshotEngine:   snapshotEngine,
   740  		timeService:      timeService,
   741  		collateralEngine: collateralEngine,
   742  	}
   743  }
   744  
   745  func (s *stubIDGen) NextID() string {
   746  	s.calls++
   747  	return hex.EncodeToString([]byte(fmt.Sprintf("deadb33f%d", s.calls)))
   748  }