code.vegaprotocol.io/vega@v0.79.0/core/processor/abci_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 processor_test 17 18 import ( 19 "context" 20 "fmt" 21 "strconv" 22 "sync" 23 "testing" 24 "time" 25 26 "code.vegaprotocol.io/vega/core/blockchain/abci" 27 "code.vegaprotocol.io/vega/core/events" 28 "code.vegaprotocol.io/vega/core/genesis" 29 "code.vegaprotocol.io/vega/core/netparams" 30 "code.vegaprotocol.io/vega/core/processor" 31 "code.vegaprotocol.io/vega/core/processor/mocks" 32 "code.vegaprotocol.io/vega/logging" 33 "code.vegaprotocol.io/vega/paths" 34 proto "code.vegaprotocol.io/vega/protos/vega" 35 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 36 37 tmtypes "github.com/cometbft/cometbft/abci/types" 38 "github.com/golang/mock/gomock" 39 "github.com/pkg/errors" 40 "github.com/stretchr/testify/require" 41 gproto "google.golang.org/protobuf/proto" 42 ) 43 44 func TestListSnapshots(t *testing.T) { 45 ctx, cfunc := context.WithCancel(context.Background()) 46 app := getTestApp(t, cfunc, stopDummy, true, true) 47 defer app.ctrl.Finish() 48 49 app.snap.EXPECT().ListLatestSnapshots().Times(1).Return([]*tmtypes.Snapshot{ 50 { 51 Height: 123, 52 Format: 1, 53 Chunks: 3, 54 Hash: []byte("0xDEADBEEF"), 55 Metadata: []byte("test"), 56 }, 57 }, nil) 58 resp, err := app.ListSnapshots(ctx, nil) 59 require.NoError(t, err) 60 require.NotNil(t, resp) 61 require.Equal(t, 1, len(resp.GetSnapshots())) 62 } 63 64 func TestAppInfo(t *testing.T) { 65 ctx, cfunc := context.WithCancel(context.Background()) 66 app := getTestApp(t, cfunc, stopDummy, true, true) 67 defer app.ctrl.Finish() 68 // first, the broker streaming stuff 69 app.broker.EXPECT().SetStreaming(false).Times(1).Return(true) 70 app.broker.EXPECT().SetStreaming(true).Times(1).Return(true) 71 // snapshot engine 72 app.snap.EXPECT().HasSnapshots().Times(1).Return(true, nil) 73 // hash, height, chainID := app.snapshotEngine.Info() 74 app.snap.EXPECT().Info().Times(1).Return([]byte("43f86066fe13743448442022c099c48abbd7e9c5eac1c2558fdac1fbf549e867"), int64(123), fmt.Sprintf("%d", app.pChainID)) 75 info, err := app.Info(ctx, nil) 76 require.NoError(t, err) 77 require.NotNil(t, info) 78 } 79 80 func getTransaction(t *testing.T, inputData *commandspb.InputData) *commandspb.Transaction { 81 t.Helper() 82 rawInputData, err := gproto.Marshal(inputData) 83 if err != nil { 84 t.Fatal(err) 85 } 86 return &commandspb.Transaction{ 87 InputData: rawInputData, 88 Signature: &commandspb.Signature{ 89 Algo: "vega/ed25519", 90 Value: "876e46defc40030391b5feb2c9bb0b6b68b2d95a6b5fd17a730a46ea73f3b1808420c8c609be6f1c6156e472ecbcd09202f750da000dee41429947a4b7eca00b", 91 Version: 1, 92 }, 93 From: &commandspb.Transaction_PubKey{ 94 PubKey: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 95 }, 96 Version: 2, 97 } 98 } 99 100 // A batch transaction including only cancellations and/or post-only limit orders is executed at 101 // the top of the block alongside standalone post-only limit orders and cancellations (0093-TRTO-001). 102 func TestBatchOnlyCancelsAndPostOnly(t *testing.T) { 103 _, cfunc := context.WithCancel(context.Background()) 104 app := getTestApp(t, cfunc, stopDummy, false, false) 105 defer app.ctrl.Finish() 106 107 // setup some order as the first tx 108 tx1InputData := &commandspb.InputData{ 109 Nonce: 123456789, 110 BlockHeight: 1789, 111 Command: &commandspb.InputData_OrderSubmission{ 112 OrderSubmission: &commandspb.OrderSubmission{ 113 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 114 Side: proto.Side_SIDE_BUY, 115 Size: 1, 116 TimeInForce: proto.Order_TIME_IN_FORCE_FOK, 117 Type: proto.Order_TYPE_LIMIT, 118 Price: "123", 119 }, 120 }, 121 } 122 tx1 := getTransaction(t, tx1InputData) 123 marshalledTx1, err := gproto.Marshal(tx1) 124 require.NoError(t, err) 125 126 // setup a batch transaction with cancellation and post only 127 tx2InputData := &commandspb.InputData{ 128 Nonce: 123456789, 129 BlockHeight: 1789, 130 Command: &commandspb.InputData_BatchMarketInstructions{ 131 BatchMarketInstructions: &commandspb.BatchMarketInstructions{ 132 Cancellations: []*commandspb.OrderCancellation{ 133 { 134 OrderId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 135 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 136 }, 137 }, 138 Submissions: []*commandspb.OrderSubmission{ 139 { 140 MarketId: "926df3b689a5440fe21cad7069ebcedc46f75b2b23ce11002a1ee2254e339f23", 141 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 142 PostOnly: true, 143 }, 144 }, 145 }, 146 }, 147 } 148 tx2 := getTransaction(t, tx2InputData) 149 marshalledTx2, err := gproto.Marshal(tx2) 150 require.NoError(t, err) 151 152 rawTxs := [][]byte{marshalledTx1, marshalledTx2} 153 txs := []abci.Tx{} 154 for _, tx := range rawTxs { 155 decodedTx, err := app.codec.Decode(tx, "1") 156 require.NoError(t, err) 157 txs = append(txs, decodedTx) 158 } 159 160 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return(nil).Times(1) 161 app.txCache.EXPECT().IsDelayRequired(gomock.Any()).Return(true).AnyTimes() 162 app.txCache.EXPECT().NewDelayedTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("123")).Times(1) 163 app.limits.EXPECT().CanTrade().Return(true).AnyTimes() 164 blockTxs := app.Abci().OnPrepareProposal(100, txs, rawTxs) 165 require.Equal(t, 2, len(blockTxs)) 166 require.Equal(t, rawTxs[1], blockTxs[0]) 167 require.Equal(t, []byte("123"), blockTxs[1]) 168 } 169 170 // A batch transaction including either a non-post-only order or an amendment is delayed by one block 171 // and then executed after the expedited transactions in that later block (0093-TRTO-002). 172 func TestBatchDelayed(t *testing.T) { 173 _, cfunc := context.WithCancel(context.Background()) 174 app := getTestApp(t, cfunc, stopDummy, false, false) 175 defer app.ctrl.Finish() 176 177 // setup some order as the first tx that doesn't get delayed 178 tx1InputData := &commandspb.InputData{ 179 Nonce: 123456789, 180 BlockHeight: 1789, 181 Command: &commandspb.InputData_OrderSubmission{ 182 OrderSubmission: &commandspb.OrderSubmission{ 183 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 184 Side: proto.Side_SIDE_BUY, 185 Size: 1, 186 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 187 Type: proto.Order_TYPE_LIMIT, 188 Price: "123", 189 PostOnly: true, 190 }, 191 }, 192 } 193 tx1 := getTransaction(t, tx1InputData) 194 marshalledTx1, err := gproto.Marshal(tx1) 195 require.NoError(t, err) 196 197 // setup a cancellation 198 tx2InputData := &commandspb.InputData{ 199 Nonce: 123456789, 200 BlockHeight: 1789, 201 Command: &commandspb.InputData_OrderCancellation{ 202 OrderCancellation: &commandspb.OrderCancellation{ 203 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 204 }, 205 }, 206 } 207 tx2 := getTransaction(t, tx2InputData) 208 marshalledTx2, err := gproto.Marshal(tx2) 209 require.NoError(t, err) 210 211 // now get a batch transaction with submission such that will get it delayed by 1 block 212 // setup a batch transaction with cancellation and post only 213 tx3InputData := &commandspb.InputData{ 214 Nonce: 123456789, 215 BlockHeight: 1789, 216 Command: &commandspb.InputData_BatchMarketInstructions{ 217 BatchMarketInstructions: &commandspb.BatchMarketInstructions{ 218 Submissions: []*commandspb.OrderSubmission{ 219 { 220 MarketId: "926df3b689a5440fe21cad7069ebcedc46f75b2b23ce11002a1ee2254e339f23", 221 Side: proto.Side_SIDE_BUY, 222 Size: 1, 223 TimeInForce: proto.Order_TIME_IN_FORCE_FOK, 224 Type: proto.Order_TYPE_LIMIT, 225 Price: "123", 226 }, 227 { 228 MarketId: "926df3b689a5440fe21cad7069ebcedc46f75b2b23ce11002a1ee2254e339f23", 229 Side: proto.Side_SIDE_BUY, 230 Size: 2, 231 TimeInForce: proto.Order_TIME_IN_FORCE_FOK, 232 Type: proto.Order_TYPE_LIMIT, 233 Price: "234", 234 }, 235 }, 236 }, 237 }, 238 } 239 tx3 := getTransaction(t, tx3InputData) 240 marshalledTx3, err := gproto.Marshal(tx3) 241 require.NoError(t, err) 242 243 rawTxs := [][]byte{marshalledTx1, marshalledTx2, marshalledTx3} 244 txs := []abci.Tx{} 245 for _, tx := range rawTxs { 246 decodedTx, err := app.codec.Decode(tx, "1") 247 require.NoError(t, err) 248 txs = append(txs, decodedTx) 249 } 250 251 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return(nil).Times(1) 252 app.txCache.EXPECT().IsDelayRequired(gomock.Any()).Return(true).AnyTimes() 253 app.txCache.EXPECT().NewDelayedTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("123")).Times(1) 254 app.limits.EXPECT().CanTrade().Return(true).AnyTimes() 255 blockTxs := app.Abci().OnPrepareProposal(100, txs, rawTxs) 256 // the first two transactions and the delayed wrapped transaction 257 require.Equal(t, 3, len(blockTxs)) 258 require.Equal(t, rawTxs[1], blockTxs[0]) 259 require.Equal(t, rawTxs[0], blockTxs[1]) 260 require.Equal(t, []byte("123"), blockTxs[2]) 261 262 // setup a cancellation 263 tx4InputData := &commandspb.InputData{ 264 Nonce: 123456789, 265 BlockHeight: 1789, 266 Command: &commandspb.InputData_OrderCancellation{ 267 OrderCancellation: &commandspb.OrderCancellation{ 268 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 269 }, 270 }, 271 } 272 tx4 := getTransaction(t, tx4InputData) 273 marshalledTx4, err := gproto.Marshal(tx4) 274 require.NoError(t, err) 275 276 rawTxs = [][]byte{marshalledTx4} 277 txs = []abci.Tx{} 278 for _, tx := range rawTxs { 279 decodedTx, err := app.codec.Decode(tx, "1") 280 require.NoError(t, err) 281 txs = append(txs, decodedTx) 282 } 283 // now lets go to the next block and have the postponed transactions executed after the 284 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return([][]byte{marshalledTx3}).Times(1) 285 blockTxs = app.Abci().OnPrepareProposal(101, txs, rawTxs) 286 require.Equal(t, 2, len(blockTxs)) 287 require.Equal(t, marshalledTx4, blockTxs[0]) 288 // the delayed transaction gets in execution order after the expedited 289 require.Equal(t, marshalledTx3, blockTxs[1]) 290 } 291 292 // Cancellation transactions always occur before: 293 // Market orders (0093-TRTO-003) 294 // Non post-only limit orders (0093-TRTO-004) 295 // Order Amends (0093-TRTO-005) 296 // post-only limit orders (0093-TRTO-013). 297 func TestCancelledOrdersGoFirst(t *testing.T) { 298 _, cfunc := context.WithCancel(context.Background()) 299 app := getTestApp(t, cfunc, stopDummy, false, false) 300 defer app.ctrl.Finish() 301 302 // cancel 1 303 // setup a cancellation 304 cancel1 := &commandspb.InputData{ 305 Nonce: 123456789, 306 BlockHeight: 1789, 307 Command: &commandspb.InputData_OrderCancellation{ 308 OrderCancellation: &commandspb.OrderCancellation{ 309 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 310 }, 311 }, 312 } 313 cancel1Tx := getTransaction(t, cancel1) 314 marshalledCancel1Tx, err := gproto.Marshal(cancel1Tx) 315 require.NoError(t, err) 316 317 // cancel 2 318 cancel2 := &commandspb.InputData{ 319 Nonce: 123456789, 320 BlockHeight: 1789, 321 Command: &commandspb.InputData_OrderCancellation{ 322 OrderCancellation: &commandspb.OrderCancellation{ 323 MarketId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 324 }, 325 }, 326 } 327 cancel2Tx := getTransaction(t, cancel2) 328 marshalledCancel2Tx, err := gproto.Marshal(cancel2Tx) 329 require.NoError(t, err) 330 331 // now lets set up one order of each of the desired types 332 marketOrder := &commandspb.InputData{ 333 Nonce: 123456789, 334 BlockHeight: 1789, 335 Command: &commandspb.InputData_OrderSubmission{ 336 OrderSubmission: &commandspb.OrderSubmission{ 337 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 338 Side: proto.Side_SIDE_BUY, 339 Size: 1, 340 TimeInForce: proto.Order_TIME_IN_FORCE_FOK, 341 Type: proto.Order_TYPE_MARKET, 342 }, 343 }, 344 } 345 marketOrderTx := getTransaction(t, marketOrder) 346 marshalledMarketOrderTx, err := gproto.Marshal(marketOrderTx) 347 require.NoError(t, err) 348 349 limitOrder := &commandspb.InputData{ 350 Nonce: 123456789, 351 BlockHeight: 1789, 352 Command: &commandspb.InputData_OrderSubmission{ 353 OrderSubmission: &commandspb.OrderSubmission{ 354 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 355 Side: proto.Side_SIDE_BUY, 356 Size: 1, 357 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 358 Type: proto.Order_TYPE_LIMIT, 359 Price: "123", 360 }, 361 }, 362 } 363 limitOrderTx := getTransaction(t, limitOrder) 364 marshalledLimitOrderTx, err := gproto.Marshal(limitOrderTx) 365 require.NoError(t, err) 366 367 amend := &commandspb.InputData{ 368 Nonce: 123456789, 369 BlockHeight: 1789, 370 Command: &commandspb.InputData_OrderAmendment{ 371 OrderAmendment: &commandspb.OrderAmendment{ 372 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 373 OrderId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 374 SizeDelta: 5, 375 }, 376 }, 377 } 378 amendTx := getTransaction(t, amend) 379 marshalledAmendTx, err := gproto.Marshal(amendTx) 380 require.NoError(t, err) 381 382 postOnly := &commandspb.InputData{ 383 Nonce: 123456789, 384 BlockHeight: 1789, 385 Command: &commandspb.InputData_OrderSubmission{ 386 OrderSubmission: &commandspb.OrderSubmission{ 387 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 388 Side: proto.Side_SIDE_BUY, 389 Size: 1, 390 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 391 Type: proto.Order_TYPE_LIMIT, 392 Price: "123", 393 PostOnly: true, 394 }, 395 }, 396 } 397 postOnlyTx := getTransaction(t, postOnly) 398 marshalledPostOnlyTx, err := gproto.Marshal(postOnlyTx) 399 require.NoError(t, err) 400 401 rawTxs := [][]byte{marshalledCancel1Tx, marshalledCancel2Tx, marshalledAmendTx, marshalledMarketOrderTx, marshalledLimitOrderTx, marshalledPostOnlyTx} 402 txs := []abci.Tx{} 403 for _, tx := range rawTxs { 404 decodedTx, err := app.codec.Decode(tx, "1") 405 require.NoError(t, err) 406 txs = append(txs, decodedTx) 407 } 408 409 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return(nil).Times(1) 410 app.txCache.EXPECT().IsDelayRequired(gomock.Any()).Return(true).AnyTimes() 411 app.txCache.EXPECT().NewDelayedTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("123")).Times(1) 412 app.limits.EXPECT().CanTrade().Return(true).AnyTimes() 413 blockTxs := app.Abci().OnPrepareProposal(100, txs, rawTxs) 414 // cancel 1, then cancel 2, then post only, then wrapped delayed 415 require.Equal(t, 4, len(blockTxs)) 416 require.Equal(t, marshalledCancel1Tx, blockTxs[0]) 417 require.Equal(t, marshalledCancel2Tx, blockTxs[1]) 418 require.Equal(t, marshalledPostOnlyTx, blockTxs[2]) 419 require.Equal(t, []byte("123"), blockTxs[3]) 420 421 // now, in the following block we expect the delayed transactions to be executed, still after expedited transactions 422 // cancel 3 423 cancel3 := &commandspb.InputData{ 424 Nonce: 123456789, 425 BlockHeight: 1789, 426 Command: &commandspb.InputData_OrderCancellation{ 427 OrderCancellation: &commandspb.OrderCancellation{ 428 MarketId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 429 }, 430 }, 431 } 432 cancel3Tx := getTransaction(t, cancel3) 433 marshalledCancel3Tx, err := gproto.Marshal(cancel3Tx) 434 require.NoError(t, err) 435 436 rawTxs = [][]byte{marshalledCancel3Tx} 437 txs = []abci.Tx{} 438 for _, tx := range rawTxs { 439 decodedTx, err := app.codec.Decode(tx, "1") 440 require.NoError(t, err) 441 txs = append(txs, decodedTx) 442 } 443 // now lets go to the next block and have the postponed transactions executed after the 444 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return([][]byte{marshalledAmendTx, marshalledMarketOrderTx, marshalledLimitOrderTx}).Times(1) 445 blockTxs = app.Abci().OnPrepareProposal(101, txs, rawTxs) 446 require.Equal(t, 4, len(blockTxs)) 447 require.Equal(t, marshalledCancel3Tx, blockTxs[0]) 448 require.Equal(t, marshalledAmendTx, blockTxs[1]) 449 require.Equal(t, marshalledMarketOrderTx, blockTxs[2]) 450 require.Equal(t, marshalledLimitOrderTx, blockTxs[3]) 451 } 452 453 // Post-only transactions always occur before: 454 // Market orders (0093-TRTO-006) 455 // Non post-only limit orders (0093-TRTO-007) 456 // Order Amends (0093-TRTO-008). 457 // Potentially aggressive orders take effect on the market exactly one block after they are included 458 // in a block (i.e for an order which is included in block N it hits the market in block N+1). This is true for: 459 // Market orders (0093-TRTO-009) 460 // Non post-only limit orders (0093-TRTO-010) 461 // Order Amends (0093-TRTO-011). 462 func TestPostOnlyGoBeforeAggressive(t *testing.T) { 463 _, cfunc := context.WithCancel(context.Background()) 464 app := getTestApp(t, cfunc, stopDummy, false, false) 465 defer app.ctrl.Finish() 466 467 postOnly1 := &commandspb.InputData{ 468 Nonce: 123456789, 469 BlockHeight: 1789, 470 Command: &commandspb.InputData_OrderSubmission{ 471 OrderSubmission: &commandspb.OrderSubmission{ 472 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 473 Side: proto.Side_SIDE_BUY, 474 Size: 1, 475 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 476 Type: proto.Order_TYPE_LIMIT, 477 Price: "123", 478 PostOnly: true, 479 }, 480 }, 481 } 482 postOnly1Tx := getTransaction(t, postOnly1) 483 marshalledPostOnly1Tx, err := gproto.Marshal(postOnly1Tx) 484 require.NoError(t, err) 485 486 postOnly2 := &commandspb.InputData{ 487 Nonce: 123456789, 488 BlockHeight: 1789, 489 Command: &commandspb.InputData_OrderSubmission{ 490 OrderSubmission: &commandspb.OrderSubmission{ 491 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 492 Side: proto.Side_SIDE_BUY, 493 Size: 1, 494 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 495 Type: proto.Order_TYPE_LIMIT, 496 Price: "123", 497 PostOnly: true, 498 }, 499 }, 500 } 501 postOnly2Tx := getTransaction(t, postOnly2) 502 marshalledPostOnly2Tx, err := gproto.Marshal(postOnly2Tx) 503 require.NoError(t, err) 504 505 // cancel 1 506 cancel1 := &commandspb.InputData{ 507 Nonce: 123456789, 508 BlockHeight: 1789, 509 Command: &commandspb.InputData_OrderCancellation{ 510 OrderCancellation: &commandspb.OrderCancellation{ 511 MarketId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 512 }, 513 }, 514 } 515 cancel1Tx := getTransaction(t, cancel1) 516 marshalledCancel1Tx, err := gproto.Marshal(cancel1Tx) 517 require.NoError(t, err) 518 519 // now lets set up one order of each of the desired types 520 marketOrder := &commandspb.InputData{ 521 Nonce: 123456789, 522 BlockHeight: 1789, 523 Command: &commandspb.InputData_OrderSubmission{ 524 OrderSubmission: &commandspb.OrderSubmission{ 525 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 526 Side: proto.Side_SIDE_BUY, 527 Size: 1, 528 TimeInForce: proto.Order_TIME_IN_FORCE_FOK, 529 Type: proto.Order_TYPE_MARKET, 530 }, 531 }, 532 } 533 marketOrderTx := getTransaction(t, marketOrder) 534 marshalledMarketOrderTx, err := gproto.Marshal(marketOrderTx) 535 require.NoError(t, err) 536 537 limitOrder := &commandspb.InputData{ 538 Nonce: 123456789, 539 BlockHeight: 1789, 540 Command: &commandspb.InputData_OrderSubmission{ 541 OrderSubmission: &commandspb.OrderSubmission{ 542 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 543 Side: proto.Side_SIDE_BUY, 544 Size: 1, 545 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 546 Type: proto.Order_TYPE_LIMIT, 547 Price: "123", 548 }, 549 }, 550 } 551 limitOrderTx := getTransaction(t, limitOrder) 552 marshalledLimitOrderTx, err := gproto.Marshal(limitOrderTx) 553 require.NoError(t, err) 554 555 amend := &commandspb.InputData{ 556 Nonce: 123456789, 557 BlockHeight: 1789, 558 Command: &commandspb.InputData_OrderAmendment{ 559 OrderAmendment: &commandspb.OrderAmendment{ 560 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 561 OrderId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 562 SizeDelta: 5, 563 }, 564 }, 565 } 566 amendTx := getTransaction(t, amend) 567 marshalledAmendTx, err := gproto.Marshal(amendTx) 568 require.NoError(t, err) 569 570 rawTxs := [][]byte{marshalledAmendTx, marshalledMarketOrderTx, marshalledLimitOrderTx, marshalledPostOnly1Tx, marshalledCancel1Tx, marshalledPostOnly2Tx} 571 txs := []abci.Tx{} 572 for _, tx := range rawTxs { 573 decodedTx, err := app.codec.Decode(tx, "1") 574 require.NoError(t, err) 575 txs = append(txs, decodedTx) 576 } 577 578 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return(nil).Times(1) 579 app.txCache.EXPECT().IsDelayRequired(gomock.Any()).Return(true).AnyTimes() 580 app.txCache.EXPECT().NewDelayedTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("123")).Times(1) 581 app.limits.EXPECT().CanTrade().Return(true).AnyTimes() 582 blockTxs := app.Abci().OnPrepareProposal(100, txs, rawTxs) 583 // cancel, then post only 1, then post only 2, then wrapped delayed 584 require.Equal(t, 4, len(blockTxs)) 585 require.Equal(t, marshalledCancel1Tx, blockTxs[0]) 586 require.Equal(t, marshalledPostOnly1Tx, blockTxs[1]) 587 require.Equal(t, marshalledPostOnly2Tx, blockTxs[2]) 588 require.Equal(t, []byte("123"), blockTxs[3]) 589 590 // now, in the following block we expect the delayed transactions to be executed, still after expedited transactions 591 // cancel 3 592 cancel3 := &commandspb.InputData{ 593 Nonce: 123456789, 594 BlockHeight: 1789, 595 Command: &commandspb.InputData_OrderCancellation{ 596 OrderCancellation: &commandspb.OrderCancellation{ 597 MarketId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 598 }, 599 }, 600 } 601 cancel3Tx := getTransaction(t, cancel3) 602 marshalledCancel3Tx, err := gproto.Marshal(cancel3Tx) 603 require.NoError(t, err) 604 605 postOnly3 := &commandspb.InputData{ 606 Nonce: 123456789, 607 BlockHeight: 1789, 608 Command: &commandspb.InputData_OrderSubmission{ 609 OrderSubmission: &commandspb.OrderSubmission{ 610 MarketId: "47076f002ddd9bfeb7f4679fc75b4686f64446d5a5afcb84584e7c7166d13efa", 611 Side: proto.Side_SIDE_BUY, 612 Size: 1, 613 TimeInForce: proto.Order_TIME_IN_FORCE_GTC, 614 Type: proto.Order_TYPE_LIMIT, 615 Price: "123", 616 PostOnly: true, 617 }, 618 }, 619 } 620 postOnly3Tx := getTransaction(t, postOnly3) 621 marshalledPostOnly3Tx, err := gproto.Marshal(postOnly3Tx) 622 require.NoError(t, err) 623 624 amend2 := &commandspb.InputData{ 625 Nonce: 123456789, 626 BlockHeight: 1789, 627 Command: &commandspb.InputData_OrderAmendment{ 628 OrderAmendment: &commandspb.OrderAmendment{ 629 MarketId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 630 OrderId: "b5fd9d3c4ad553cb3196303b6e6df7f484cf7f5331a572a45031239fd71ad8a0", 631 SizeDelta: 5, 632 }, 633 }, 634 } 635 amend2Tx := getTransaction(t, amend2) 636 marshalledAmend2Tx, err := gproto.Marshal(amend2Tx) 637 require.NoError(t, err) 638 639 rawTxs = [][]byte{marshalledPostOnly3Tx, marshalledAmend2Tx, marshalledCancel3Tx} 640 txs = []abci.Tx{} 641 for _, tx := range rawTxs { 642 decodedTx, err := app.codec.Decode(tx, "1") 643 require.NoError(t, err) 644 txs = append(txs, decodedTx) 645 } 646 // now lets go to the next block and have the postponed transactions executed after the new cancellation and post only from this block 647 // plus throw in another amend this block that gets delayed to the next block 648 app.txCache.EXPECT().GetRawTxs(gomock.Any()).Return([][]byte{marshalledAmendTx, marshalledMarketOrderTx, marshalledLimitOrderTx}).Times(1) 649 app.txCache.EXPECT().NewDelayedTransaction(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("456")).Times(1) 650 blockTxs = app.Abci().OnPrepareProposal(101, txs, rawTxs) 651 require.Equal(t, 6, len(blockTxs)) 652 require.Equal(t, marshalledCancel3Tx, blockTxs[0]) 653 require.Equal(t, marshalledPostOnly3Tx, blockTxs[1]) 654 require.Equal(t, marshalledAmendTx, blockTxs[2]) 655 require.Equal(t, marshalledMarketOrderTx, blockTxs[3]) 656 require.Equal(t, marshalledLimitOrderTx, blockTxs[4]) 657 require.Equal(t, []byte("456"), blockTxs[5]) 658 } 659 660 type tstApp struct { 661 *processor.App 662 ctrl *gomock.Controller 663 timeSvc *mocks.MockTimeService 664 epochSvc *mocks.MockEpochService 665 delegation *mocks.MockDelegationEngine 666 exec *mocks.MockExecutionEngine 667 gov *mocks.MockGovernanceEngine 668 stats *mocks.MockStats 669 assets *mocks.MockAssets 670 validator *mocks.MockValidatorTopology 671 notary *mocks.MockNotary 672 evtForwarder *mocks.MockEvtForwarder 673 witness *mocks.MockWitness 674 banking *mocks.MockBanking 675 netParams *mocks.MockNetworkParameters 676 oracleEngine *mocks.MockOraclesEngine 677 oracleAdaptors *mocks.MockOracleAdaptors 678 l1Verifier *mocks.MockEthereumOracleVerifier 679 l2Verifier *mocks.MockEthereumOracleVerifier 680 limits *mocks.MockLimits 681 stakeVerifier *mocks.MockStakeVerifier 682 stakingAccs *mocks.MockStakingAccounts 683 primaryERC20 *mocks.MockERC20MultiSigTopology 684 secondaryERC20 *mocks.MockERC20MultiSigTopology 685 cp *mocks.MockCheckpoint 686 broker *mocks.MockBroker 687 evtForwarderHB *mocks.MockEvtForwarderHeartbeat 688 spam *mocks.MockSpamEngine 689 pow *mocks.MockPoWEngine 690 snap *mocks.MockSnapshotEngine 691 stateVar *mocks.MockStateVarEngine 692 teams *mocks.MockTeamsEngine 693 referral *mocks.MockReferralProgram 694 volDiscount *mocks.MockVolumeDiscountProgram 695 volRebate *mocks.MockVolumeRebateProgram 696 bClient *mocks.MockBlockchainClient 697 puSvc *mocks.MockProtocolUpgradeService 698 ethCallEng *mocks.MockEthCallEngine 699 balance *mocks.MockBalanceChecker 700 parties *mocks.MockPartiesEngine 701 txCache *mocks.MockTxCache 702 codec processor.NullBlockchainTxCodec 703 onTickCB []func(context.Context, time.Time) 704 pChainID, sChainID uint64 705 } 706 707 func TestProtocolUpgradeFailedBrokerStreamError(t *testing.T) { 708 streamClient := newBrokerClient(0) 709 wg := sync.WaitGroup{} 710 wg.Add(1) 711 stop := func() error { 712 wg.Done() 713 return nil 714 } 715 ctx, cfunc := context.WithCancel(context.Background()) 716 app := getTestAppWithInit(t, cfunc, stop, false, false) 717 defer func() { 718 wg.Wait() 719 streamClient.finish() 720 app.ctrl.Finish() 721 }() 722 723 // vars 724 blockHeight := uint64(123) 725 blockHash := "0xDEADBEEF" 726 proposer := "0xCAFECAFE1" 727 updateTime := time.Now() 728 brokerErr := errors.Errorf("pretend something went wrong") 729 730 // let's make this look like a protocol upgrade 731 app.txCache.EXPECT().SetRawTxs(nil, blockHeight).Times(1) 732 733 // These are the calls made in the startProtocolUpgrade func: 734 app.puSvc.EXPECT().CoreReadyForUpgrade().Times(1).Return(true) 735 // broker streaming is enabled, so let's set up the channels and push some data 736 app.broker.EXPECT().StreamingEnabled().Times(1).Return(true) 737 app.broker.EXPECT().SocketClient().Times(1).Return(streamClient) 738 // we expect the protocol upgraded started event to be sent once at least. 739 app.broker.EXPECT().Send(gomock.Any()).Times(1) 740 // as part of the event data sent here, we call stats to get the height: 741 app.stats.EXPECT().Height().Times(1).Return(blockHeight - 1) 742 // now we set the upgrade service as ready: 743 app.puSvc.EXPECT().SetReadyForUpgrade().Times(0) 744 745 // start stream client routine, throw some events on the channels 746 go func() { 747 // this event should be read, but not have any effect on the logic. 748 streamClient.evtCh <- events.NewTime(ctx, updateTime) 749 require.True(t, blockHeight > 0) // just check we reach this part 750 streamClient.errCh <- brokerErr 751 }() 752 753 // see if we can recover here? 754 defer func() { 755 if r := recover(); r != nil { 756 expect := "failed to wait for data node to get ready for upgrade" 757 msg := fmt.Sprintf("Test likely passed, recovered: %v\n", r) 758 require.Contains(t, msg, expect, msg) 759 } 760 }() 761 762 // start upgrade 763 _ = app.OnBeginBlock(blockHeight, blockHash, updateTime, proposer, nil) 764 } 765 766 func TestOnBeginBlock(t *testing.T) { 767 // keeping the test for reference, as it can be used for reference with regular OnBeginBlock calls 768 // or error cases with for protocol upgrade stuff. 769 // set up stop call, so we can ensure the chain is stopped as part of protocol upgrade. 770 _, cfunc := context.WithCancel(context.Background()) 771 app := getTestAppWithInit(t, cfunc, stopDummy, true, true) 772 defer app.ctrl.Finish() 773 774 // vars 775 blockHeight := uint64(123) 776 blockHash := "0xDEADBEEF" 777 proposer := "0xCAFECAFE1" 778 updateTime := time.Now() 779 prevTime := updateTime.Add(-1 * time.Second) 780 781 // let's make this look like a protocol upgrade 782 app.txCache.EXPECT().SetRawTxs(nil, blockHeight).Times(1) 783 784 // check for upgrade 785 app.puSvc.EXPECT().CoreReadyForUpgrade().Times(1).Return(false) 786 787 // now we're back to the OnBeginBlock call, set mocks for the remainder of that func: 788 app.broker.EXPECT().Send(gomock.Any()).Times(1) 789 // we're not passing any transactions ATM, so no setTxStats to worry about 790 // now PoW 791 app.pow.EXPECT().BeginBlock(blockHeight, blockHash, nil).Times(1) 792 // spam: 793 app.spam.EXPECT().BeginBlock(nil).Times(1) 794 // now do stats: 795 app.stats.EXPECT().SetHash(blockHash).Times(1) 796 app.stats.EXPECT().SetHeight(blockHeight).Times(1) 797 // now the calls to time service: 798 app.timeSvc.EXPECT().SetTimeNow(gomock.Any(), updateTime).Times(1) 799 app.timeSvc.EXPECT().GetTimeNow().Times(1).Return(updateTime) 800 app.timeSvc.EXPECT().GetTimeLastBatch().Times(1).Return(prevTime) 801 // begin upgrade: 802 app.puSvc.EXPECT().BeginBlock(gomock.Any(), blockHeight).Times(1) 803 // topology: 804 app.validator.EXPECT().BeginBlock(gomock.Any(), blockHeight, proposer).Times(1) 805 // balance checker: 806 app.balance.EXPECT().BeginBlock(gomock.Any()).Times(1) 807 // exec engine: 808 app.exec.EXPECT().BeginBlock(gomock.Any(), updateTime.Sub(prevTime)).Times(1) 809 810 // actually make the call now: 811 ctx := app.OnBeginBlock(blockHeight, blockHash, updateTime, proposer, nil) 812 // we should get a context back, this just checks the call returned. 813 require.NotNil(t, ctx) 814 } 815 816 func stopDummy() error { 817 return nil 818 } 819 820 func getTestApp(t *testing.T, cfunc func(), stop func() error, PoW, Spam bool) *tstApp { 821 t.Helper() 822 pChain := "1" 823 sChain := "2" 824 gHandler := genesis.New( 825 logging.NewTestLogger(), 826 genesis.NewDefaultConfig(), 827 ) 828 pChainID, err := strconv.ParseUint(pChain, 10, 64) 829 if err != nil { 830 t.Fatalf("Could not get test app instance, chain ID parse error: %v", err) 831 } 832 sChainID, err := strconv.ParseUint(sChain, 10, 64) 833 if err != nil { 834 t.Fatalf("Could not get test app instance, chain ID parse error: %v", err) 835 } 836 ctrl := gomock.NewController(t) 837 broker := mocks.NewMockBroker(ctrl) 838 timeSvc := mocks.NewMockTimeService(ctrl) 839 epochSvc := mocks.NewMockEpochService(ctrl) 840 delegation := mocks.NewMockDelegationEngine(ctrl) 841 exec := mocks.NewMockExecutionEngine(ctrl) 842 gov := mocks.NewMockGovernanceEngine(ctrl) 843 stats := mocks.NewMockStats(ctrl) 844 assets := mocks.NewMockAssets(ctrl) 845 validator := mocks.NewMockValidatorTopology(ctrl) 846 notary := mocks.NewMockNotary(ctrl) 847 evtForwarder := mocks.NewMockEvtForwarder(ctrl) 848 evtForwarderHB := mocks.NewMockEvtForwarderHeartbeat(ctrl) 849 witness := mocks.NewMockWitness(ctrl) 850 banking := mocks.NewMockBanking(ctrl) 851 netParams := mocks.NewMockNetworkParameters(ctrl) 852 oracleEngine := mocks.NewMockOraclesEngine(ctrl) 853 oracleAdaptors := mocks.NewMockOracleAdaptors(ctrl) 854 l1Verifier := mocks.NewMockEthereumOracleVerifier(ctrl) 855 l2Verifier := mocks.NewMockEthereumOracleVerifier(ctrl) 856 limits := mocks.NewMockLimits(ctrl) 857 stakeVerifier := mocks.NewMockStakeVerifier(ctrl) 858 stakingAccs := mocks.NewMockStakingAccounts(ctrl) 859 pERC20 := mocks.NewMockERC20MultiSigTopology(ctrl) 860 sERC20 := mocks.NewMockERC20MultiSigTopology(ctrl) 861 cp := mocks.NewMockCheckpoint(ctrl) 862 var ( 863 spam *mocks.MockSpamEngine 864 pow *mocks.MockPoWEngine 865 ) 866 if Spam { 867 spam = mocks.NewMockSpamEngine(ctrl) 868 } 869 if PoW { 870 pow = mocks.NewMockPoWEngine(ctrl) 871 } 872 snap := mocks.NewMockSnapshotEngine(ctrl) 873 stateVar := mocks.NewMockStateVarEngine(ctrl) 874 teams := mocks.NewMockTeamsEngine(ctrl) 875 referral := mocks.NewMockReferralProgram(ctrl) 876 volDiscount := mocks.NewMockVolumeDiscountProgram(ctrl) 877 volRebate := mocks.NewMockVolumeRebateProgram(ctrl) 878 bClient := mocks.NewMockBlockchainClient(ctrl) 879 puSvc := mocks.NewMockProtocolUpgradeService(ctrl) 880 ethCallEng := mocks.NewMockEthCallEngine(ctrl) 881 balance := mocks.NewMockBalanceChecker(ctrl) 882 parties := mocks.NewMockPartiesEngine(ctrl) 883 txCache := mocks.NewMockTxCache(ctrl) 884 codec := processor.NullBlockchainTxCodec{} 885 // paths, config, gastimator, ... 886 vp := paths.New("/tmp") 887 conf := processor.NewDefaultConfig() 888 gastimator := processor.NewGastimator(exec) 889 // test wrapper 890 tstApp := &tstApp{ 891 ctrl: ctrl, 892 broker: broker, 893 timeSvc: timeSvc, 894 epochSvc: epochSvc, 895 delegation: delegation, 896 exec: exec, 897 gov: gov, 898 stats: stats, 899 assets: assets, 900 validator: validator, 901 notary: notary, 902 evtForwarder: evtForwarder, 903 evtForwarderHB: evtForwarderHB, 904 witness: witness, 905 banking: banking, 906 netParams: netParams, 907 oracleEngine: oracleEngine, 908 oracleAdaptors: oracleAdaptors, 909 l1Verifier: l1Verifier, 910 l2Verifier: l2Verifier, 911 limits: limits, 912 stakeVerifier: stakeVerifier, 913 stakingAccs: stakingAccs, 914 primaryERC20: pERC20, 915 secondaryERC20: sERC20, 916 cp: cp, 917 spam: spam, 918 pow: pow, 919 snap: snap, 920 stateVar: stateVar, 921 teams: teams, 922 referral: referral, 923 volDiscount: volDiscount, 924 volRebate: volRebate, 925 bClient: bClient, 926 puSvc: puSvc, 927 ethCallEng: ethCallEng, 928 balance: balance, 929 parties: parties, 930 txCache: txCache, 931 codec: codec, 932 onTickCB: []func(context.Context, time.Time){}, 933 pChainID: pChainID, 934 sChainID: sChainID, 935 } 936 // timeSvc will be set up to the onTick callback 937 timeSvc.EXPECT().NotifyOnTick(gomock.Any()).AnyTimes().Do(func(cbs ...func(context.Context, time.Time)) { 938 if cbs == nil { 939 return 940 } 941 tstApp.onTickCB = append(tstApp.onTickCB, cbs...) 942 }) 943 // ensureConfig calls netparams 944 netParams.EXPECT().GetJSONStruct(netparams.BlockchainsPrimaryEthereumConfig, gomock.Any()).Times(1).DoAndReturn(func(_ string, v netparams.Reset) error { 945 vt, ok := v.(*proto.EthereumConfig) 946 if !ok { 947 return errors.Errorf("invalid type %t", v) 948 } 949 vt.ChainId = pChain 950 return nil 951 }) 952 netParams.EXPECT().GetJSONStruct(netparams.BlockchainsEVMBridgeConfigs, gomock.Any()).Times(1).DoAndReturn(func(_ string, v netparams.Reset) error { 953 vt, ok := v.(*proto.EVMBridgeConfigs) 954 if !ok { 955 return errors.Errorf("invalid type %t", v) 956 } 957 if vt.Configs == nil { 958 vt.Configs = []*proto.EVMBridgeConfig{} 959 } 960 vt.Configs = append(vt.Configs, &proto.EVMBridgeConfig{ 961 ChainId: sChain, 962 }) 963 return nil 964 }) 965 // set primary chain ID 966 exec.EXPECT().OnChainIDUpdate(pChainID).Times(1).Return(nil) 967 gov.EXPECT().OnChainIDUpdate(pChainID).Times(1).Return(nil) 968 app := processor.NewApp( 969 logging.NewTestLogger(), 970 vp, 971 conf, 972 cfunc, 973 stop, 974 assets, 975 banking, 976 broker, 977 witness, 978 evtForwarder, 979 evtForwarderHB, 980 exec, 981 gHandler, 982 gov, 983 notary, 984 stats, 985 timeSvc, 986 epochSvc, 987 validator, 988 netParams, 989 &processor.Oracle{ 990 Engine: oracleEngine, 991 Adaptors: oracleAdaptors, 992 EthereumOraclesVerifier: l1Verifier, 993 EthereumL2OraclesVerifier: l2Verifier, 994 }, 995 delegation, 996 limits, 997 stakeVerifier, 998 cp, 999 spam, 1000 pow, 1001 stakingAccs, 1002 snap, 1003 stateVar, 1004 teams, 1005 referral, 1006 volDiscount, 1007 volRebate, 1008 bClient, 1009 pERC20, 1010 sERC20, 1011 "0", 1012 puSvc, 1013 &codec, 1014 gastimator, 1015 ethCallEng, 1016 balance, 1017 parties, 1018 txCache, 1019 ) 1020 1021 // embed the app 1022 tstApp.App = app 1023 // return wrapper 1024 return tstApp 1025 } 1026 1027 func getTestAppWithInit(t *testing.T, cfunc func(), stop func() error, PoW, Spam bool) *tstApp { 1028 t.Helper() 1029 tstApp := getTestApp(t, cfunc, stop, PoW, Spam) 1030 // now set up the OnInitChain stuff 1031 req := &tmtypes.RequestInitChain{ 1032 ChainId: "1", 1033 InitialHeight: 0, 1034 Time: time.Now().Add(-1 * time.Hour), // some time in the past 1035 } 1036 // set up mock calls: 1037 tstApp.broker.EXPECT().Send(gomock.Any()).Times(2) // once before, and once after gHandler is called 1038 tstApp.ethCallEng.EXPECT().Start().Times(1) 1039 tstApp.validator.EXPECT().GetValidatorPowerUpdates().Times(1).Return(nil) 1040 resp, err := tstApp.OnInitChain(req) 1041 require.NoError(t, err) 1042 require.NotNil(t, resp) 1043 return tstApp 1044 } 1045 1046 // fake socket client. 1047 type brokerClient struct { 1048 errCh chan error 1049 evtCh chan events.Event 1050 } 1051 1052 func newBrokerClient(buf int) *brokerClient { 1053 return &brokerClient{ 1054 errCh: make(chan error, buf), 1055 evtCh: make(chan events.Event, buf), 1056 } 1057 } 1058 1059 func (b *brokerClient) finish() { 1060 if b.errCh != nil { 1061 close(b.errCh) 1062 b.errCh = nil 1063 } 1064 if b.evtCh != nil { 1065 close(b.evtCh) 1066 b.evtCh = nil 1067 } 1068 } 1069 1070 func (b *brokerClient) SendBatch(events []events.Event) error { 1071 return nil 1072 } 1073 1074 func (b *brokerClient) Receive(ctx context.Context) (<-chan events.Event, <-chan error) { 1075 return b.evtCh, b.errCh 1076 }