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 }