diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cecb8cd33..1ba894a6c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - [8414](https://github.com/vegaprotocol/vega/issues/8414) - Fix corruption on order subscription - [8418](https://github.com/vegaprotocol/vega/issues/8418) - Fix data node panics when a bad successor market proposal is rejected - [8358](https://github.com/vegaprotocol/vega/issues/8358) - Fix replay protection +- [8451](https://github.com/vegaprotocol/vega/issues/8451) - Fix invalid auction duration for new market proposals. ## 0.71.0 diff --git a/core/execution/engine.go b/core/execution/engine.go index a1b656085d..860389caac 100644 --- a/core/execution/engine.go +++ b/core/execution/engine.go @@ -369,8 +369,8 @@ func (e *Engine) IsEligibleForProposerBonus(marketID string, value *num.Uint) bo } // SubmitMarket submits a new market configuration to the network. -func (e *Engine) SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string) error { - return e.submitOrRestoreMarket(ctx, marketConfig, proposer, true) +func (e *Engine) SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string, oos time.Time) error { + return e.submitOrRestoreMarket(ctx, marketConfig, proposer, true, oos) } // RestoreMarket restores a new market from proposal checkpoint. @@ -379,7 +379,8 @@ func (e *Engine) RestoreMarket(ctx context.Context, marketConfig *types.Market) if len(proposer) == 0 { return ErrMarketDoesNotExist } - if err := e.submitOrRestoreMarket(ctx, marketConfig, "", false); err != nil { + // restoring a market means starting it as though the proposal was accepted now. + if err := e.submitOrRestoreMarket(ctx, marketConfig, "", false, e.timeService.GetTimeNow()); err != nil { return err } // attempt to restore market state. The restoreOwnState call handles both parent and successor markets @@ -394,7 +395,7 @@ func (e *Engine) RestoreMarket(ctx context.Context, marketConfig *types.Market) return nil } -func (e *Engine) submitOrRestoreMarket(ctx context.Context, marketConfig *types.Market, proposer string, isNewMarket bool) error { +func (e *Engine) submitOrRestoreMarket(ctx context.Context, marketConfig *types.Market, proposer string, isNewMarket bool, oos time.Time) error { if e.log.IsDebug() { msg := "submit market" if !isNewMarket { @@ -403,7 +404,7 @@ func (e *Engine) submitOrRestoreMarket(ctx context.Context, marketConfig *types. e.log.Debug(msg, logging.Market(*marketConfig)) } - if err := e.submitMarket(ctx, marketConfig); err != nil { + if err := e.submitMarket(ctx, marketConfig, oos); err != nil { return err } if pid := marketConfig.ParentMarketID; len(pid) > 0 { @@ -462,13 +463,11 @@ func (e *Engine) publishUpdateMarketInfos(ctx context.Context, mkt *future.Marke } // submitMarket will submit a new market configuration to the network. -func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market) error { +func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market, oos time.Time) error { if len(marketConfig.ID) == 0 { return ErrNoMarketID } - now := e.timeService.GetTimeNow() - // ensure the asset for this new market exists assets, err := marketConfig.GetAssets() if err != nil { @@ -483,7 +482,7 @@ func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market) e } // create market auction state - mas := monitor.NewAuctionState(marketConfig, now) + mas := monitor.NewAuctionState(marketConfig, oos) ad, err := e.assets.Get(asset) if err != nil { e.log.Error("Failed to create a new market, unknown asset", diff --git a/core/execution/engine_snapshot_test.go b/core/execution/engine_snapshot_test.go index 313f942a36..b482c3286e 100644 --- a/core/execution/engine_snapshot_test.go +++ b/core/execution/engine_snapshot_test.go @@ -16,6 +16,7 @@ import ( "bytes" "context" "testing" + "time" vegapb "code.vegaprotocol.io/vega/protos/vega" datapb "code.vegaprotocol.io/vega/protos/vega/data/v1" @@ -271,7 +272,7 @@ func TestValidMarketSnapshot(t *testing.T) { assert.NotNil(t, engine) marketConfig := getMarketConfig() - err := engine.SubmitMarket(ctx, marketConfig, "") + err := engine.SubmitMarket(ctx, marketConfig, "", time.Now()) assert.NoError(t, err) keys := engine.Keys() diff --git a/core/execution/engine_test.go b/core/execution/engine_test.go index d8d3ae8cf5..0de909bf7b 100644 --- a/core/execution/engine_test.go +++ b/core/execution/engine_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "code.vegaprotocol.io/vega/core/assets" "code.vegaprotocol.io/vega/core/events" @@ -58,7 +59,7 @@ func TestMarketSuccession(t *testing.T) { exec.timeSvc.EXPECT().GetTimeNow().AnyTimes() // create parent market - err := exec.SubmitMarket(ctx, mkt, "") + err := exec.SubmitMarket(ctx, mkt, "", time.Now()) require.NoError(t, err) // create successors @@ -74,9 +75,9 @@ func TestMarketSuccession(t *testing.T) { child2.InsurancePoolFraction = num.DecimalFromFloat(.33) child2.State = types.MarketStateProposed // submit successor markets - err = exec.SubmitMarket(ctx, child1, "") + err = exec.SubmitMarket(ctx, child1, "", time.Now()) require.NoError(t, err) - err = exec.SubmitMarket(ctx, child2, "") + err = exec.SubmitMarket(ctx, child2, "", time.Now()) require.NoError(t, err) // when enacting a successor market, a lot of stuff happens: diff --git a/core/execution/future/market.go b/core/execution/future/market.go index d67bca2808..95d3a3a435 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -235,16 +235,17 @@ func NewMarket( mkt.State = types.MarketStateProposed mkt.TradingMode = types.MarketTradingModeNoTrading + pending, open := as.GetAuctionBegin(), as.GetAuctionEnd() // Populate the market timestamps ts := &types.MarketTimestamps{ Proposed: now.UnixNano(), Pending: now.UnixNano(), } - - if mkt.OpeningAuction != nil { - ts.Open = now.Add(time.Duration(mkt.OpeningAuction.Duration)).UnixNano() - } else { - ts.Open = now.UnixNano() + if pending != nil { + ts.Pending = pending.UnixNano() + } + if open != nil { + ts.Open = open.UnixNano() } mkt.MarketTimestamps = ts @@ -567,7 +568,8 @@ func (m *Market) StartOpeningAuction(ctx context.Context) error { if m.as.AuctionStart() { // we are now in a pending state m.mkt.State = types.MarketStatePending - m.mkt.MarketTimestamps.Pending = m.timeService.GetTimeNow().UnixNano() + // this should no longer be needed + // m.mkt.MarketTimestamps.Pending = m.timeService.GetTimeNow().UnixNano() m.mkt.TradingMode = types.MarketTradingModeOpeningAuction m.enterAuction(ctx) } else { diff --git a/core/execution/snapshot_test.go b/core/execution/snapshot_test.go index e9f2646aee..218ece161b 100644 --- a/core/execution/snapshot_test.go +++ b/core/execution/snapshot_test.go @@ -62,7 +62,7 @@ func TestSnapshotOraclesTerminatingMarketFromSnapshot(t *testing.T) { }, } mkt := newMarket("MarketID", pubKey) - err := exec.engine.SubmitMarket(context.Background(), mkt, "") + err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now()) require.NoError(t, err) state, _, _ := exec.engine.GetState("") @@ -137,7 +137,7 @@ func TestSnapshotOraclesTerminatingMarketSettleAfterSnapshot(t *testing.T) { }, } mkt := newMarket("MarketID", pubKey) - err := exec.engine.SubmitMarket(context.Background(), mkt, "") + err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now()) require.NoError(t, err) err = exec.engine.StartOpeningAuction(context.Background(), mkt.ID) @@ -265,7 +265,7 @@ func TestSnapshotOraclesTerminatingMarketFromSnapshotAfterSettlementData(t *test now := time.Now() exec := getEngine(t, now) mkt := newMarket("MarketID", pubKeys[0].Signer.(*types.SignerPubKey)) - err := exec.engine.SubmitMarket(context.Background(), mkt, "") + err := exec.engine.SubmitMarket(context.Background(), mkt, "", time.Now()) require.NoError(t, err) err = exec.engine.StartOpeningAuction(context.Background(), mkt.ID) @@ -332,7 +332,7 @@ func TestLoadTerminatedMarketFromSnapshot(t *testing.T) { // submit and terminate all markets for i := 0; i < 3; i++ { mkt := newMarket(marketIDs[i], pubKeys[i].Signer.(*types.SignerPubKey)) - err := exec.engine.SubmitMarket(ctx, mkt, "") + err := exec.engine.SubmitMarket(ctx, mkt, "", time.Now()) require.NoError(t, err) // verify markets are terminated diff --git a/core/governance/checkpoint.go b/core/governance/checkpoint.go index 2cd37589a4..921c7c4a3c 100644 --- a/core/governance/checkpoint.go +++ b/core/governance/checkpoint.go @@ -29,6 +29,7 @@ import ( type enactmentTime struct { current int64 shouldNotVerify bool + cpLoad bool } type firstSuccessor struct { @@ -112,7 +113,9 @@ func (e *Engine) Load(ctx context.Context, data []byte) error { // for the successor market not to have a parent in the execution engine continue } - enct := &enactmentTime{} + enct := &enactmentTime{ + cpLoad: true, + } // if the proposal is for a new market it should be restored it such that it will be in opening auction if toEnact { prop.Terms.EnactmentTimestamp = now.Add(duration).Unix() diff --git a/core/governance/engine.go b/core/governance/engine.go index 14998f41be..7d40f9d2d4 100644 --- a/core/governance/engine.go +++ b/core/governance/engine.go @@ -599,13 +599,15 @@ func (e *Engine) intoToSubmit(ctx context.Context, p *types.Proposal, enct *enac } parent = &pm } - closeTime := e.timeService.GetTimeNow().Truncate(time.Second) + closeTime := time.Unix(p.Terms.ClosingTimestamp, 0) enactTime := time.Unix(p.Terms.EnactmentTimestamp, 0) auctionDuration := enactTime.Sub(closeTime) if perr, err := validateNewMarketChange(newMarket, e.assets, true, e.netp, auctionDuration, enct, parent); err != nil { e.rejectProposal(ctx, p, perr, err) return nil, fmt.Errorf("%w, %v", err, perr) } + // closeTime = e.timeService.GetTimeNow().Round(time.Second) + // auctionDuration = enactTime.Sub(closeTime) mkt, perr, err := buildMarketFromProposal(p.ID, newMarket, e.netp, auctionDuration) if err != nil { e.rejectProposal(ctx, p, perr, err) diff --git a/core/governance/market.go b/core/governance/market.go index e4d14fc50d..8bfd959b9c 100644 --- a/core/governance/market.go +++ b/core/governance/market.go @@ -581,7 +581,7 @@ func validateNewMarketChange( return perr, err } // verify opening auction duration, works the same for successor markets - if perr, err := validateAuctionDuration(openingAuctionDuration, netp); err != nil { + if perr, err := validateAuctionDuration(openingAuctionDuration, netp); !etu.cpLoad && err != nil { return perr, err } // if this is a successor market, check if that's set up fine: diff --git a/core/governance/proposal.go b/core/governance/proposal.go index 45e2c8c532..bd1a0800cf 100644 --- a/core/governance/proposal.go +++ b/core/governance/proposal.go @@ -210,13 +210,18 @@ func (t *ToSubmit) InsurancePoolFraction() *num.Decimal { } type ToSubmitNewMarket struct { - m *types.Market + m *types.Market + oos time.Time // opening auction start } func (t *ToSubmitNewMarket) Market() *types.Market { return t.m } +func (t *ToSubmitNewMarket) OpeningAuctionStart() time.Time { + return t.oos +} + type VoteClosed struct { p *types.Proposal m *NewMarketVoteClosed diff --git a/core/integration/main_test.go b/core/integration/main_test.go index 3054d1991a..673d8d56c2 100644 --- a/core/integration/main_test.go +++ b/core/integration/main_test.go @@ -143,7 +143,7 @@ func InitializeScenario(s *godog.ScenarioContext) { return steps.TheMarginCalculator(marketConfig, name, table) }) s.Step(`^the markets:$`, func(table *godog.Table) error { - markets, err := steps.TheMarkets(marketConfig, execsetup.executionEngine, execsetup.collateralEngine, execsetup.netParams, table) + markets, err := steps.TheMarkets(marketConfig, execsetup.executionEngine, execsetup.collateralEngine, execsetup.netParams, execsetup.timeService.GetTimeNow(), table) execsetup.markets = markets return err }) diff --git a/core/integration/steps/execution.go b/core/integration/steps/execution.go index 90f2c1fbdd..8679901837 100644 --- a/core/integration/steps/execution.go +++ b/core/integration/steps/execution.go @@ -14,6 +14,7 @@ package steps import ( "context" + "time" "code.vegaprotocol.io/vega/core/types" "code.vegaprotocol.io/vega/libs/num" @@ -31,7 +32,7 @@ type Execution interface { deterministicID string) error AmendLiquidityProvision(ctx context.Context, amendment *types.LiquidityProvisionAmendment, party string) error CancelLiquidityProvision(ctx context.Context, cancel *types.LiquidityProvisionCancellation, party string) error - SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string) error + SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string, oos time.Time) error StartOpeningAuction(ctx context.Context, marketID string) error UpdateMarket(ctx context.Context, marketConfig *types.Market) error BlockEnd(ctx context.Context) diff --git a/core/integration/steps/the_markets.go b/core/integration/steps/the_markets.go index 35ede3afeb..aef2866317 100644 --- a/core/integration/steps/the_markets.go +++ b/core/integration/steps/the_markets.go @@ -15,6 +15,7 @@ package steps import ( "context" "fmt" + "time" "github.com/cucumber/godog" "github.com/pkg/errors" @@ -65,6 +66,7 @@ func TheMarkets( executionEngine Execution, collateralEngine *collateral.Engine, netparams *netparams.Store, + now time.Time, table *godog.Table, ) ([]types.Market, error) { rows := parseMarketsTable(table) @@ -83,7 +85,7 @@ func TheMarkets( return nil, err } - if err := submitMarkets(markets, executionEngine); err != nil { + if err := submitMarkets(markets, executionEngine, now); err != nil { return nil, err } @@ -108,7 +110,7 @@ func TheSuccessorMarkets( } // submit the successor markets and start opening auction as we tend to do - if err := submitMarkets(markets, exec); err != nil { + if err := submitMarkets(markets, exec, time.Now()); err != nil { return nil, err } return markets, nil @@ -127,9 +129,9 @@ func TheSuccesorMarketIsEnacted(sID string, markets []types.Market, exec Executi return fmt.Errorf("couldn't enact successor market %s - no such market ID", sID) } -func submitMarkets(markets []types.Market, executionEngine Execution) error { +func submitMarkets(markets []types.Market, executionEngine Execution, now time.Time) error { for i := range markets { - if err := executionEngine.SubmitMarket(context.Background(), &markets[i], "proposerID"); err != nil { + if err := executionEngine.SubmitMarket(context.Background(), &markets[i], "proposerID", now); err != nil { return fmt.Errorf("couldn't submit market(%s): %v", markets[i].ID, err) } if err := executionEngine.StartOpeningAuction(context.Background(), markets[i].ID); err != nil { diff --git a/core/monitor/auction.go b/core/monitor/auction.go index 9059999a9b..c308da85c3 100644 --- a/core/monitor/auction.go +++ b/core/monitor/auction.go @@ -61,6 +61,23 @@ func NewAuctionState(mkt *types.Market, now time.Time) *AuctionState { return &s } +func (a *AuctionState) GetAuctionBegin() *time.Time { + if a.begin == nil { + return nil + } + cpy := *a.begin + return &cpy +} + +func (a *AuctionState) GetAuctionEnd() *time.Time { + if a.begin == nil || a.end == nil { + return nil + } + cpy := *a.begin + cpy = cpy.Add(time.Duration(a.end.Duration) * time.Second) + return &cpy +} + func (a *AuctionState) StartLiquidityAuctionNoOrders(t time.Time, d *types.AuctionDuration) { a.startLiquidityAuction(t, d, types.AuctionTriggerUnableToDeployLPOrders) } diff --git a/core/processor/abci.go b/core/processor/abci.go index 4e046e2d35..6680420928 100644 --- a/core/processor/abci.go +++ b/core/processor/abci.go @@ -1483,10 +1483,12 @@ func (app *App) DeliverPropose(ctx context.Context, tx abci.Tx, deterministicID } if toSubmit.IsNewMarket() { + // opening auction start + oos := time.Unix(toSubmit.Proposal().Terms.ClosingTimestamp, 0).Round(time.Second) nm := toSubmit.NewMarket() // @TODO pass in parent and insurance pool share if required - if err := app.exec.SubmitMarket(ctx, nm.Market(), party); err != nil { + if err := app.exec.SubmitMarket(ctx, nm.Market(), party, oos); err != nil { app.log.Debug("unable to submit new market with liquidity submission", logging.ProposalID(nm.Market().ID), logging.Error(err)) diff --git a/core/processor/mocks/mocks.go b/core/processor/mocks/mocks.go index c482585784..ded4831fbc 100644 --- a/core/processor/mocks/mocks.go +++ b/core/processor/mocks/mocks.go @@ -392,17 +392,17 @@ func (mr *MockExecutionEngineMockRecorder) SubmitLiquidityProvision(arg0, arg1, } // SubmitMarket mocks base method. -func (m *MockExecutionEngine) SubmitMarket(arg0 context.Context, arg1 *types.Market, arg2 string) error { +func (m *MockExecutionEngine) SubmitMarket(arg0 context.Context, arg1 *types.Market, arg2 string, arg3 time.Time) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitMarket", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "SubmitMarket", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // SubmitMarket indicates an expected call of SubmitMarket. -func (mr *MockExecutionEngineMockRecorder) SubmitMarket(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockExecutionEngineMockRecorder) SubmitMarket(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitMarket", reflect.TypeOf((*MockExecutionEngine)(nil).SubmitMarket), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitMarket", reflect.TypeOf((*MockExecutionEngine)(nil).SubmitMarket), arg0, arg1, arg2, arg3) } // SubmitOrder mocks base method. diff --git a/core/processor/processor.go b/core/processor/processor.go index de775839c0..8d4be33bcc 100644 --- a/core/processor/processor.go +++ b/core/processor/processor.go @@ -73,7 +73,7 @@ type ExecutionEngine interface { AmendOrder(ctx context.Context, order *types.OrderAmendment, party string, idgen common.IDGenerator) (*types.OrderConfirmation, error) // market stuff - SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string) error + SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string, oos time.Time) error UpdateMarket(ctx context.Context, marketConfig *types.Market) error RejectMarket(ctx context.Context, marketid string) error StartOpeningAuction(ctx context.Context, marketid string) error