code.vegaprotocol.io/vega@v0.79.0/core/execution/engine_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  	"context"
    20  	"encoding/hex"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"code.vegaprotocol.io/vega/core/assets"
    26  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    27  	"code.vegaprotocol.io/vega/core/datasource/spec"
    28  	"code.vegaprotocol.io/vega/core/events"
    29  	"code.vegaprotocol.io/vega/core/types"
    30  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    31  	"code.vegaprotocol.io/vega/libs/num"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func TestMarketSuccession(t *testing.T) {
    38  	exec := getMockedEngine(t)
    39  	exec.timeSvc.EXPECT().GetTimeNow().AnyTimes().Return(time.Now())
    40  	exec.OnSuccessorMarketTimeWindowUpdate(context.Background(), 100*time.Second)
    41  	defer exec.ctrl.Finish()
    42  	knownAssets := map[string]*assets.Asset{}
    43  	mkt := getMarketConfig()
    44  	mkt.ID = "parentID"
    45  	// sendCount, batchCount := 0, 0
    46  	ctx := vgcontext.WithTraceID(context.Background(), hex.EncodeToString([]byte("0deadbeef")))
    47  	// for now, we don't care about this much
    48  	// exec.epoch.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).AnyTimes()
    49  	exec.asset.EXPECT().Get(gomock.Any()).AnyTimes().DoAndReturn(func(asset string) (*assets.Asset, error) {
    50  		a, ok := knownAssets[asset]
    51  		if !ok {
    52  			a = NewAssetStub(asset, 0)
    53  			knownAssets[asset] = a
    54  		}
    55  		if a == nil {
    56  			return nil, fmt.Errorf("unknown asset")
    57  		}
    58  		return a, nil
    59  	})
    60  	// this is to propose the parent market and the 2 successors, and starting the opening auction for the eventual successor
    61  	seen := false
    62  	exec.broker.EXPECT().Send(gomock.Any()).AnyTimes().Do(func(e events.Event) {
    63  		if e.Type() == events.MarketUpdatedEvent {
    64  			seen = true
    65  		}
    66  	})
    67  	exec.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
    68  	exec.collateral.EXPECT().GetAssetQuantum("Ethereum/Ether").AnyTimes().Return(num.DecimalFromInt64(1), nil)
    69  	exec.collateral.EXPECT().AssetExists(gomock.Any()).AnyTimes().Return(true)
    70  	exec.collateral.EXPECT().CreateMarketAccounts(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
    71  	exec.oracle.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(spec.SubscriptionID(0), func(_ context.Context, _ spec.SubscriptionID) {}, nil)
    72  	exec.statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
    73  	exec.statevar.EXPECT().NewEvent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
    74  
    75  	// create parent market
    76  	err := exec.SubmitMarket(ctx, mkt, "", time.Now())
    77  	require.NoError(t, err)
    78  
    79  	// create successors
    80  	child1 := getMarketConfig()
    81  	child1.ParentMarketID = mkt.ID
    82  	child1.ID = "child1"
    83  	child1.InsurancePoolFraction = num.DecimalFromFloat(.5)
    84  	child1.State = types.MarketStateProposed
    85  
    86  	child2 := getMarketConfig()
    87  	child2.ParentMarketID = mkt.ID
    88  	child2.ID = "child2"
    89  	child2.InsurancePoolFraction = num.DecimalFromFloat(.33)
    90  	child2.State = types.MarketStateActive
    91  	// submit successor markets
    92  	err = exec.SubmitMarket(ctx, child1, "", time.Now())
    93  	require.NoError(t, err)
    94  	err = exec.SubmitMarket(ctx, child2, "", time.Now())
    95  	require.NoError(t, err)
    96  
    97  	// when enacting a successor market, a lot of stuff happens:
    98  
    99  	// Transfer insurance pool fraction
   100  	acc := &types.Account{
   101  		Balance: num.UintZero(),
   102  	}
   103  	exec.collateral.EXPECT().GetMarketLiquidityFeeAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(acc, nil)
   104  	// exec.collateral.EXPECT().SuccessorInsuranceFraction(ctx, child1.ID, child1.ParentMarketID, gomock.Any(), child1.InsurancePoolFraction).Times(1).Return(&types.LedgerMovement{})
   105  	// which in turn emits an event with ledger movements
   106  	// we get the parent market state to pass in the ELS and stuff:
   107  	exec.collateral.EXPECT().GetInsurancePoolBalance(child1.ParentMarketID, gomock.Any()).AnyTimes().Return(num.NewUint(100), true) // the balance doesn't matter for this test
   108  	exec.collateral.EXPECT().GetInsurancePoolBalance(child1.ID, gomock.Any()).AnyTimes().Return(num.NewUint(50), true)              // the balance doesn't matter for this test
   109  	exec.collateral.EXPECT().GetInsurancePoolBalance(child2.ID, gomock.Any()).AnyTimes().Return(num.NewUint(50), true)              // the balance doesn't matter for this test
   110  	// Any accounts associated with the now rejected successor market will be removed
   111  	// exec.collateral.EXPECT().ClearMarket(ctx, child2.ID, gomock.Any(), gomock.Any()).Times(1).Return(nil, nil)
   112  	// statevars associated with the rejected successor market are unregistered
   113  	exec.statevar.EXPECT().UnregisterStateVariable(gomock.Any(), child2.ID).AnyTimes()
   114  	// the other succesor markets are rejected and removed, which emits market update events
   115  	// set parent market to be settled
   116  	err = exec.StartOpeningAuction(ctx, child1.ID)
   117  	require.NoError(t, err)
   118  	mkt.State = types.MarketStateSettled
   119  	child1.State = types.MarketStateProposed
   120  	// start opening auction for the successor market
   121  	err = exec.SucceedMarket(ctx, child1.ID, child1.ParentMarketID)
   122  	require.NoError(t, err)
   123  	exec.OnTick(ctx, time.Now())
   124  	require.True(t, seen)
   125  }
   126  
   127  func TestUpdateMarginUpdate(t *testing.T) {
   128  	engine, ctrl := createEngine(t)
   129  	defer ctrl.Finish()
   130  
   131  	require.Equal(t, types.ErrInvalidMarketID, engine.UpdateMarginMode(context.Background(), "zohar", "unknown", types.MarginModeIsolatedMargin, num.DecimalOne()))
   132  
   133  	pubKey := &dstypes.SignerPubKey{
   134  		PubKey: &dstypes.PubKey{
   135  			Key: "0xDEADBEEF",
   136  		},
   137  	}
   138  	mkt := newMarket("market-id", pubKey)
   139  	mkt.LinearSlippageFactor = num.DecimalFromFloat(0.1)
   140  	require.NoError(t, engine.SubmitMarket(context.Background(), mkt, "zohar", time.Now()))
   141  
   142  	// rfShort, rfLong = 1
   143  	require.Equal(t, "margin factor (0.5) must be greater than max(riskFactorLong (1), riskFactorShort (1)) + linearSlippageFactor (0.1)", engine.UpdateMarginMode(context.Background(), "zohar", "market-id", types.MarginModeIsolatedMargin, num.DecimalFromFloat(0.5)).Error())
   144  	require.Equal(t, "margin factor (1.05) must be greater than max(riskFactorLong (1), riskFactorShort (1)) + linearSlippageFactor (0.1)", engine.UpdateMarginMode(context.Background(), "zohar", "market-id", types.MarginModeIsolatedMargin, num.DecimalFromFloat(1.05)).Error())
   145  }