github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/sequencer/finalizer_test.go (about) 1 package sequencer 2 3 import ( 4 "context" 5 "fmt" 6 "math/big" 7 "sync" 8 "testing" 9 "time" 10 11 cfgTypes "github.com/0xPolygon/supernets2-node/config/types" 12 "github.com/0xPolygon/supernets2-node/event" 13 "github.com/0xPolygon/supernets2-node/event/nileventstorage" 14 "github.com/0xPolygon/supernets2-node/pool" 15 "github.com/0xPolygon/supernets2-node/state" 16 "github.com/0xPolygon/supernets2-node/state/runtime/executor" 17 "github.com/0xPolygon/supernets2-node/state/runtime/executor/pb" 18 "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/core/types" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/mock" 22 "github.com/stretchr/testify/require" 23 ) 24 25 var ( 26 f *finalizer 27 nilErr error 28 dbManagerMock = new(DbManagerMock) 29 executorMock = new(StateMock) 30 workerMock = new(WorkerMock) 31 dbTxMock = new(DbTxMock) 32 bc = batchConstraints{ 33 MaxTxsPerBatch: 150, 34 MaxBatchBytesSize: 129848, 35 MaxCumulativeGasUsed: 30000000, 36 MaxKeccakHashes: 468, 37 MaxPoseidonHashes: 279620, 38 MaxPoseidonPaddings: 149796, 39 MaxMemAligns: 262144, 40 MaxArithmetics: 262144, 41 MaxBinaries: 262144, 42 MaxSteps: 8388608, 43 } 44 txsStore = TxsStore{ 45 Ch: make(chan *txToStore, 1), 46 Wg: new(sync.WaitGroup), 47 } 48 closingSignalCh = ClosingSignalCh{ 49 ForcedBatchCh: make(chan state.ForcedBatch), 50 GERCh: make(chan common.Hash), 51 L2ReorgCh: make(chan L2ReorgEvent), 52 } 53 cfg = FinalizerCfg{ 54 GERDeadlineTimeout: cfgTypes.Duration{ 55 Duration: 60, 56 }, 57 ForcedBatchDeadlineTimeout: cfgTypes.Duration{ 58 Duration: 60, 59 }, 60 SleepDuration: cfgTypes.Duration{ 61 Duration: 60, 62 }, 63 ClosingSignalsManagerWaitForCheckingL1Timeout: cfgTypes.Duration{ 64 Duration: 10 * time.Second, 65 }, 66 ClosingSignalsManagerWaitForCheckingGER: cfgTypes.Duration{ 67 Duration: 10 * time.Second, 68 }, 69 ClosingSignalsManagerWaitForCheckingForcedBatches: cfgTypes.Duration{ 70 Duration: 10 * time.Second, 71 }, 72 ResourcePercentageToCloseBatch: 10, 73 GERFinalityNumberOfBlocks: 64, 74 } 75 seqAddr = common.Address{} 76 oldHash = common.HexToHash("0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f2") 77 newHash = common.HexToHash("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 78 sender = common.HexToAddress("0x3445324") 79 isSynced = func(ctx context.Context) bool { 80 return true 81 } 82 // tx1 = ethTypes.NewTransaction(0, common.HexToAddress("0"), big.NewInt(0), 0, big.NewInt(0), []byte("aaa")) 83 // tx2 = ethTypes.NewTransaction(1, common.HexToAddress("1"), big.NewInt(1), 0, big.NewInt(1), []byte("bbb")) 84 85 testErr = fmt.Errorf("some error") 86 // testErr2 = fmt.Errorf("some error2") 87 openBatchError = fmt.Errorf("failed to open new batch, err: %w", testErr) 88 cumulativeGasErr = state.GetZKCounterError("CumulativeGasUsed") 89 ) 90 91 func testNow() time.Time { 92 return time.Unix(0, 0) 93 } 94 95 func TestNewFinalizer(t *testing.T) { 96 eventStorage, err := nileventstorage.NewNilEventStorage() 97 require.NoError(t, err) 98 eventLog := event.NewEventLog(event.Config{}, eventStorage) 99 100 // arrange and act 101 f = newFinalizer(cfg, workerMock, dbManagerMock, executorMock, seqAddr, isSynced, closingSignalCh, txsStore, bc, eventLog) 102 103 // assert 104 assert.NotNil(t, f) 105 assert.Equal(t, f.cfg, cfg) 106 assert.Equal(t, f.worker, workerMock) 107 assert.Equal(t, f.dbManager, dbManagerMock) 108 assert.Equal(t, f.executor, executorMock) 109 assert.Equal(t, f.sequencerAddress, seqAddr) 110 assert.Equal(t, f.closingSignalCh, closingSignalCh) 111 assert.Equal(t, f.txsStore, txsStore) 112 assert.Equal(t, f.batchConstraints, bc) 113 } 114 115 // 116 //func TestFinalizer_newWIPBatch(t *testing.T) { 117 // // arrange 118 // f = setupFinalizer(true) 119 // now = testNow 120 // defer func() { 121 // now = time.Now 122 // }() 123 // 124 // txs := make([]types.Transaction, 0) 125 // batchNum := f.batch.batchNumber + 1 126 // f.processRequest.GlobalExitRoot = oldHash 127 // f.processRequest.OldStateRoot = oldHash 128 // f.processRequest.Transactions = []byte{} 129 // expectedWipBatch := &WipBatch{ 130 // batchNumber: batchNum, 131 // coinbase: f.sequencerAddress, 132 // initialStateRoot: newHash, 133 // stateRoot: newHash, 134 // timestamp: uint64(now().Unix()), 135 // remainingResources: getMaxRemainingResources(f.batchConstraints), 136 // } 137 // closeBatchParams := ClosingBatchParameters{ 138 // BatchNumber: f.batch.batchNumber, 139 // StateRoot: f.batch.stateRoot, 140 // LocalExitRoot: f.batch.localExitRoot, 141 // Txs: txs, 142 // } 143 // batches := []*state.Batch{ 144 // { 145 // BatchNumber: 0, 146 // StateRoot: oldHash, 147 // }, 148 // } 149 // testCases := []struct { 150 // name string 151 // batches []*state.Batch 152 // closeBatchErr error 153 // closeBatchParams ClosingBatchParameters 154 // openBatchErr error 155 // expectedWip *WipBatch 156 // expectedErr error 157 // }{ 158 // { 159 // name: "Success", 160 // expectedWip: expectedWipBatch, 161 // closeBatchParams: closeBatchParams, 162 // batches: batches, 163 // }, 164 // { 165 // name: "Close Batch Error", 166 // expectedWip: expectedWipBatch, 167 // closeBatchParams: closeBatchParams, 168 // batches: batches, 169 // closeBatchErr: testErr, 170 // expectedErr: fmt.Errorf("failed to close batch, err: %w", testErr), 171 // }, 172 // { 173 // name: "Open Batch Error", 174 // expectedWip: expectedWipBatch, 175 // closeBatchParams: closeBatchParams, 176 // batches: batches, 177 // openBatchErr: testErr, 178 // expectedErr: fmt.Errorf("failed to open new batch, err: %w", testErr), 179 // }, 180 // } 181 // for _, tc := range testCases { 182 // t.Run(tc.name, func(t *testing.T) { 183 // // arrange 184 // dbManagerMock.On("CloseBatch", ctx, tc.closeBatchParams).Return(tc.closeBatchErr).Once() 185 // executorMock.On("ProcessBatch", ctx, f.processRequest).Return(&state.ProcessBatchResponse{ 186 // IsBatchProcessed: true, 187 // }, nilErr).Once() 188 // 189 // if tc.expectedErr == nil { 190 // dbManagerMock.On("GetTransactionsByBatchNumber", ctx, f.batch.batchNumber).Return(txs, nilErr).Once() 191 // } 192 // 193 // if tc.closeBatchErr == nil { 194 // dbManagerMock.On("BeginStateTransaction", ctx).Return(dbTxMock, nilErr).Once() 195 // dbManagerMock.On("OpenBatch", ctx, mock.Anything, dbTxMock).Return(tc.openBatchErr).Once() 196 // 197 // // Async Calls from reprocessBatch 198 // dbManagerMock.On("GetLastNBatches", ctx, uint(2)).Return(tc.batches, nilErr).Maybe() 199 // dbManagerMock.On("GetTransactionsByBatchNumber", ctx, f.batch.batchNumber).Return(txs, nilErr).Maybe() 200 // processRequest := f.processRequest 201 // processRequest.Caller = state.DiscardCallerLabel 202 // processRequest.Timestamp = f.batch.timestamp 203 // executorMock.On("ProcessBatch", ctx, processRequest).Return(&state.ProcessBatchResponse{ 204 // NewStateRoot: f.batch.stateRoot, 205 // NewLocalExitRoot: f.batch.localExitRoot, 206 // 207 // IsBatchProcessed: true, 208 // }, nilErr).Maybe() 209 // 210 // if tc.openBatchErr == nil { 211 // dbTxMock.On("Commit", ctx).Return(nilErr).Once() 212 // } else { 213 // dbTxMock.On("Rollback", ctx).Return(nilErr).Once() 214 // } 215 // } 216 // 217 // // act 218 // wipBatch, err := f.newWIPBatch(ctx) 219 // 220 // // assert 221 // if tc.expectedErr != nil { 222 // assert.Error(t, err) 223 // assert.EqualError(t, err, tc.expectedErr.Error()) 224 // assert.Nil(t, wipBatch) 225 // } else { 226 // assert.NoError(t, err) 227 // assert.Equal(t, tc.expectedWip, wipBatch) 228 // } 229 // dbManagerMock.AssertExpectations(t) 230 // dbTxMock.AssertExpectations(t) 231 // }) 232 // } 233 //} 234 235 func TestFinalizer_syncWithState(t *testing.T) { 236 // arrange 237 f = setupFinalizer(true) 238 now = testNow 239 defer func() { 240 now = time.Now 241 }() 242 one := uint64(1) 243 batches := []*state.Batch{ 244 { 245 BatchNumber: 1, 246 StateRoot: oldHash, 247 GlobalExitRoot: oldHash, 248 }, 249 } 250 testCases := []struct { 251 name string 252 batches []*state.Batch 253 lastBatchNum *uint64 254 isBatchClosed bool 255 ger common.Hash 256 getWIPBatchErr error 257 openBatchErr error 258 isBatchClosedErr error 259 getLastBatchErr error 260 expectedProcessingCtx state.ProcessingContext 261 expectedBatch *WipBatch 262 expectedErr error 263 }{ 264 { 265 name: "Success-Closed Batch", 266 lastBatchNum: &one, 267 isBatchClosed: true, 268 ger: oldHash, 269 batches: batches, 270 expectedBatch: &WipBatch{ 271 batchNumber: one + 1, 272 coinbase: f.sequencerAddress, 273 initialStateRoot: oldHash, 274 stateRoot: oldHash, 275 timestamp: testNow(), 276 globalExitRoot: oldHash, 277 remainingResources: getMaxRemainingResources(f.batchConstraints), 278 }, 279 expectedProcessingCtx: state.ProcessingContext{ 280 BatchNumber: one + 1, 281 Coinbase: f.sequencerAddress, 282 Timestamp: testNow(), 283 GlobalExitRoot: oldHash, 284 }, 285 expectedErr: nil, 286 }, 287 { 288 name: "Success-Open Batch", 289 lastBatchNum: &one, 290 isBatchClosed: false, 291 batches: batches, 292 ger: common.Hash{}, 293 expectedBatch: &WipBatch{ 294 batchNumber: one, 295 coinbase: f.sequencerAddress, 296 initialStateRoot: oldHash, 297 stateRoot: oldHash, 298 timestamp: testNow(), 299 globalExitRoot: oldHash, 300 remainingResources: getMaxRemainingResources(f.batchConstraints), 301 }, 302 expectedProcessingCtx: state.ProcessingContext{ 303 BatchNumber: one, 304 Coinbase: f.sequencerAddress, 305 Timestamp: testNow(), 306 GlobalExitRoot: oldHash, 307 }, 308 }, 309 { 310 name: "Error-Failed to get last batch", 311 lastBatchNum: nil, 312 batches: batches, 313 isBatchClosed: true, 314 ger: oldHash, 315 getLastBatchErr: testErr, 316 expectedErr: fmt.Errorf("failed to get last batch, err: %w", testErr), 317 }, 318 { 319 name: "Error-Failed to check if batch is closed", 320 lastBatchNum: &one, 321 batches: batches, 322 isBatchClosed: true, 323 ger: oldHash, 324 isBatchClosedErr: testErr, 325 expectedErr: fmt.Errorf("failed to check if batch is closed, err: %w", testErr), 326 }, 327 { 328 name: "Error-Failed to get work-in-progress batch", 329 lastBatchNum: &one, 330 batches: batches, 331 isBatchClosed: false, 332 ger: common.Hash{}, 333 getWIPBatchErr: testErr, 334 expectedErr: fmt.Errorf("failed to get work-in-progress batch, err: %w", testErr), 335 }, 336 { 337 name: "Error-Failed to open new batch", 338 lastBatchNum: &one, 339 batches: batches, 340 isBatchClosed: true, 341 ger: oldHash, 342 openBatchErr: testErr, 343 expectedProcessingCtx: state.ProcessingContext{ 344 BatchNumber: one + 1, 345 Coinbase: f.sequencerAddress, 346 Timestamp: testNow(), 347 GlobalExitRoot: oldHash, 348 }, 349 expectedErr: fmt.Errorf("failed to open new batch, err: %w", testErr), 350 }, 351 } 352 353 for _, tc := range testCases { 354 t.Run(tc.name, func(t *testing.T) { 355 // arrange 356 if tc.lastBatchNum == nil { 357 dbManagerMock.Mock.On("GetLastBatch", ctx).Return(tc.batches[0], tc.getLastBatchErr).Once() 358 } else { 359 dbManagerMock.On("GetBatchByNumber", ctx, *tc.lastBatchNum, nil).Return(tc.batches[0], nilErr).Once() 360 } 361 362 if tc.getLastBatchErr == nil { 363 dbManagerMock.Mock.On("IsBatchClosed", ctx, *tc.lastBatchNum).Return(tc.isBatchClosed, tc.isBatchClosedErr).Once() 364 } 365 366 if tc.isBatchClosed { 367 if tc.getLastBatchErr == nil && tc.isBatchClosedErr == nil { 368 dbManagerMock.On("OpenBatch", ctx, tc.expectedProcessingCtx, dbTxMock).Return(tc.openBatchErr).Once() 369 } 370 371 if tc.getLastBatchErr == nil && tc.isBatchClosedErr == nil { 372 dbManagerMock.Mock.On("GetLatestGer", ctx, f.cfg.GERFinalityNumberOfBlocks).Return(state.GlobalExitRoot{GlobalExitRoot: tc.ger}, testNow(), nil).Once() 373 dbManagerMock.On("BeginStateTransaction", ctx).Return(dbTxMock, nil).Once() 374 if tc.openBatchErr == nil { 375 dbTxMock.On("Commit", ctx).Return(nil).Once() 376 } 377 } 378 if tc.expectedErr != nil && tc.openBatchErr != nil { 379 dbTxMock.On("Rollback", ctx).Return(nil).Once() 380 } 381 } else { 382 dbManagerMock.Mock.On("GetWIPBatch", ctx).Return(tc.expectedBatch, tc.getWIPBatchErr).Once() 383 } 384 385 // act 386 err := f.syncWithState(ctx, tc.lastBatchNum) 387 388 // assert 389 if tc.expectedErr != nil { 390 assert.Error(t, err) 391 assert.EqualError(t, err, tc.expectedErr.Error()) 392 } else { 393 assert.NoError(t, err) 394 assert.Equal(t, tc.expectedBatch, f.batch) 395 } 396 dbManagerMock.AssertExpectations(t) 397 }) 398 } 399 } 400 401 //func TestFinalizer_processForcedBatches(t *testing.T) { 402 // // arrange 403 // var err error 404 // f = setupFinalizer(false) 405 // now = testNow 406 // defer func() { 407 // now = time.Now 408 // }() 409 // RawTxsData1 := make([]byte, 0, 2) 410 // RawTxsData1 = append(RawTxsData1, []byte("forced tx 1")...) 411 // RawTxsData1 = append(RawTxsData1, []byte("forced tx 2")...) 412 // RawTxsData2 := make([]byte, 0, 2) 413 // RawTxsData2 = append(RawTxsData2, []byte("forced tx 3")...) 414 // RawTxsData2 = append(RawTxsData2, []byte("forced tx 4")...) 415 // batchNumber := f.batch.batchNumber 416 // stateRoot := oldHash 417 // forcedBatch1 := state.ForcedBatch{ 418 // ForcedBatchNumber: 2, 419 // GlobalExitRoot: oldHash, 420 // RawTxsData: RawTxsData1, 421 // } 422 // forcedBatch2 := state.ForcedBatch{ 423 // ForcedBatchNumber: 3, 424 // GlobalExitRoot: oldHash, 425 // RawTxsData: RawTxsData2, 426 // } 427 // testCases := []struct { 428 // name string 429 // forcedBatch []state.ForcedBatch 430 // getLastTrustedForcedBatchNumErr error 431 // expectedErr error 432 // }{ 433 // { 434 // name: "Success", 435 // forcedBatch: []state.ForcedBatch{forcedBatch1, forcedBatch2}, 436 // }, 437 // { 438 // name: "GetLastTrustedForcedBatchNumber_Error", 439 // forcedBatch: []state.ForcedBatch{forcedBatch1}, 440 // getLastTrustedForcedBatchNumErr: testErr, 441 // expectedErr: fmt.Errorf("failed to get last trusted forced batch number, err: %s", testErr), 442 // }, 443 // } 444 // 445 // for _, tc := range testCases { 446 // t.Run(tc.name, func(t *testing.T) { 447 // // arrange 448 // f.nextForcedBatches = tc.forcedBatch 449 // internalBatchNumber := batchNumber 450 // dbManagerMock.On("BeginStateTransaction", ctx).Return(dbTxMock, nil).Once() 451 // dbManagerMock.On("GetLastTrustedForcedBatchNumber", ctx, dbTxMock).Return(uint64(1), tc.getLastTrustedForcedBatchNumErr).Once() 452 // 453 // for _, forcedBatch := range tc.forcedBatch { 454 // internalBatchNumber += 1 455 // processRequest := state.ProcessRequest{ 456 // BatchNumber: internalBatchNumber, 457 // OldStateRoot: stateRoot, 458 // GlobalExitRoot: forcedBatch.GlobalExitRoot, 459 // Transactions: forcedBatch.RawTxsData, 460 // Coinbase: f.sequencerAddress, 461 // Timestamp: now(), 462 // Caller: stateMetrics.SequencerCallerLabel, 463 // } 464 // dbManagerMock.On("ProcessForcedBatch", forcedBatch.ForcedBatchNumber, processRequest).Return(&state.ProcessBatchResponse{ 465 // NewStateRoot: stateRoot, 466 // NewBatchNumber: internalBatchNumber, 467 // }, nilErr).Once() 468 // } 469 // 470 // // act 471 // batchNumber, stateRoot, err = f.processForcedBatches(ctx, batchNumber, stateRoot) 472 // 473 // // assert 474 // if tc.expectedErr != nil { 475 // assert.EqualError(t, err, tc.expectedErr.Error()) 476 // } else { 477 // assert.NoError(t, tc.expectedErr) 478 // dbManagerMock.AssertExpectations(t) 479 // } 480 // }) 481 // } 482 //} 483 484 func TestFinalizer_openWIPBatch(t *testing.T) { 485 // arrange 486 f = setupFinalizer(true) 487 now = testNow 488 defer func() { 489 now = time.Now 490 }() 491 batchNum := f.batch.batchNumber + 1 492 expectedWipBatch := &WipBatch{ 493 batchNumber: batchNum, 494 coinbase: f.sequencerAddress, 495 initialStateRoot: oldHash, 496 stateRoot: oldHash, 497 timestamp: now(), 498 globalExitRoot: oldHash, 499 remainingResources: getMaxRemainingResources(f.batchConstraints), 500 } 501 testCases := []struct { 502 name string 503 openBatchErr error 504 beginTxErr error 505 commitErr error 506 rollbackErr error 507 expectedWip *WipBatch 508 expectedErr error 509 }{ 510 { 511 name: "Success", 512 expectedWip: expectedWipBatch, 513 }, 514 { 515 name: "BeginTransaction Error", 516 beginTxErr: testErr, 517 expectedErr: fmt.Errorf("failed to begin state transaction to open batch, err: %w", testErr), 518 }, 519 { 520 name: "OpenBatch Error", 521 openBatchErr: testErr, 522 expectedErr: fmt.Errorf("failed to open new batch, err: %w", testErr), 523 }, 524 { 525 name: "Commit Error", 526 commitErr: testErr, 527 expectedErr: fmt.Errorf("failed to commit database transaction for opening a batch, err: %w", testErr), 528 }, 529 { 530 name: "Rollback Error", 531 openBatchErr: testErr, 532 rollbackErr: testErr, 533 expectedErr: fmt.Errorf( 534 "failed to rollback dbTx: %s. Rollback err: %w", 535 testErr.Error(), openBatchError, 536 ), 537 }, 538 } 539 540 for _, tc := range testCases { 541 t.Run(tc.name, func(t *testing.T) { 542 // arrange 543 dbManagerMock.On("BeginStateTransaction", ctx).Return(dbTxMock, tc.beginTxErr).Once() 544 if tc.beginTxErr == nil { 545 dbManagerMock.On("OpenBatch", ctx, mock.Anything, dbTxMock).Return(tc.openBatchErr).Once() 546 } 547 548 if tc.expectedErr != nil && (tc.rollbackErr != nil || tc.openBatchErr != nil) { 549 dbTxMock.On("Rollback", ctx).Return(tc.rollbackErr).Once() 550 } 551 552 if tc.expectedErr == nil || tc.commitErr != nil { 553 dbTxMock.On("Commit", ctx).Return(tc.commitErr).Once() 554 } 555 556 // act 557 wipBatch, err := f.openWIPBatch(ctx, batchNum, oldHash, oldHash) 558 559 // assert 560 if tc.expectedErr != nil { 561 assert.Error(t, err) 562 assert.EqualError(t, err, tc.expectedErr.Error()) 563 assert.Nil(t, wipBatch) 564 } else { 565 assert.NoError(t, err) 566 assert.Equal(t, tc.expectedWip, wipBatch) 567 } 568 dbManagerMock.AssertExpectations(t) 569 dbTxMock.AssertExpectations(t) 570 }) 571 } 572 } 573 574 // TestFinalizer_closeBatch tests the closeBatch method. 575 func TestFinalizer_closeBatch(t *testing.T) { 576 // arrange 577 f = setupFinalizer(true) 578 txs := make([]types.Transaction, 0) 579 usedResources := getUsedBatchResources(f.batchConstraints, f.batch.remainingResources) 580 receipt := ClosingBatchParameters{ 581 BatchNumber: f.batch.batchNumber, 582 StateRoot: f.batch.stateRoot, 583 LocalExitRoot: f.processRequest.GlobalExitRoot, 584 BatchResources: usedResources, 585 Txs: txs, 586 } 587 managerErr := fmt.Errorf("some error") 588 testCases := []struct { 589 name string 590 managerErr error 591 expectedErr error 592 }{ 593 { 594 name: "Success", 595 managerErr: nil, 596 expectedErr: nil, 597 }, 598 { 599 name: "Manager Error", 600 managerErr: managerErr, 601 expectedErr: fmt.Errorf("failed to get transactions from transactions, err: %w", managerErr), 602 }, 603 } 604 605 for _, tc := range testCases { 606 t.Run(tc.name, func(t *testing.T) { 607 // arrange 608 dbManagerMock.Mock.On("CloseBatch", ctx, receipt).Return(tc.managerErr).Once() 609 dbManagerMock.Mock.On("GetTransactionsByBatchNumber", ctx, receipt.BatchNumber).Return(txs, tc.managerErr).Once() 610 611 // act 612 err := f.closeBatch(ctx) 613 614 // assert 615 if tc.expectedErr != nil { 616 assert.Error(t, err) 617 assert.EqualError(t, err, tc.expectedErr.Error()) 618 assert.ErrorIs(t, err, tc.managerErr) 619 } else { 620 assert.NoError(t, err) 621 } 622 }) 623 } 624 } 625 626 func TestFinalizer_openBatch(t *testing.T) { 627 // arrange 628 f = setupFinalizer(true) 629 now = testNow 630 defer func() { 631 now = time.Now 632 }() 633 batchNum := f.batch.batchNumber + 1 634 testCases := []struct { 635 name string 636 batchNum uint64 637 managerErr error 638 expectedCtx state.ProcessingContext 639 expectedErr error 640 }{ 641 { 642 name: "Success", 643 batchNum: batchNum, 644 managerErr: nil, 645 expectedCtx: state.ProcessingContext{ 646 BatchNumber: batchNum, 647 Coinbase: f.sequencerAddress, 648 Timestamp: now(), 649 GlobalExitRoot: oldHash, 650 }, 651 expectedErr: nil, 652 }, 653 { 654 name: "Manager Error", 655 batchNum: batchNum, 656 managerErr: testErr, 657 expectedCtx: state.ProcessingContext{}, 658 expectedErr: openBatchError, 659 }, 660 } 661 662 for _, tc := range testCases { 663 t.Run(tc.name, func(t *testing.T) { 664 // arrange 665 dbManagerMock.Mock.On("OpenBatch", mock.Anything, mock.Anything, mock.Anything).Return(tc.managerErr).Once() 666 667 // act 668 actualCtx, err := f.openBatch(ctx, tc.batchNum, oldHash, nil) 669 670 // assert 671 if tc.expectedErr != nil { 672 assert.Error(t, err) 673 assert.EqualError(t, err, tc.expectedErr.Error()) 674 assert.ErrorIs(t, err, tc.managerErr) 675 assert.Empty(t, actualCtx) 676 } else { 677 assert.NoError(t, err) 678 assert.Equal(t, tc.expectedCtx, actualCtx) 679 } 680 dbManagerMock.AssertExpectations(t) 681 }) 682 } 683 } 684 685 /* 686 // TestFinalizer_reprocessBatch is a test for reprocessBatch which tests all possible cases of reprocessBatch 687 func TestFinalizer_reprocessBatch(t *testing.T) { 688 // arrange 689 now = testNow 690 defer func() { 691 now = time.Now 692 }() 693 694 f = setupFinalizer(true) 695 n := uint(2) 696 expectedProcessBatchRequest := state.ProcessRequest{ 697 BatchNumber: f.batch.batchNumber, 698 OldStateRoot: oldHash, 699 Coinbase: f.sequencerAddress, 700 Timestamp: f.batch.timestamp, 701 Caller: state.DiscardCallerLabel, 702 } 703 batches := []*state.Batch{ 704 { 705 BatchNumber: f.batch.batchNumber, 706 StateRoot: newHash, 707 Timestamp: testNow(), 708 }, 709 { 710 BatchNumber: f.batch.batchNumber - 1, 711 GlobalExitRoot: oldHash, 712 StateRoot: oldHash, 713 Timestamp: testNow(), 714 }, 715 } 716 717 // TODO: Add missing cases for this test 718 testCases := []struct { 719 name string 720 getLastNBatchesErr error 721 processBatchErr error 722 batches []*state.Batch 723 expectedErr error 724 internalErr error 725 expectedProcessRequest state.ProcessRequest 726 expectedProcessBatchResult *state.ProcessBatchResponse 727 }{ 728 { 729 name: "Success", 730 batches: batches, 731 expectedProcessRequest: expectedProcessBatchRequest, 732 expectedProcessBatchResult: &state.ProcessBatchResponse{ 733 NewStateRoot: newHash, 734 IsBatchProcessed: true, 735 }, 736 }, 737 { 738 name: "GetLastNBatches Error", 739 getLastNBatchesErr: testErr, 740 internalErr: testErr, 741 expectedErr: fmt.Errorf("failed to get old state root, err: failed to get last %d batches, err: %w", n, testErr), 742 }, 743 { 744 name: "ProcessBatch Error", 745 processBatchErr: testErr, 746 internalErr: testErr, 747 expectedErr: testErr, 748 expectedProcessRequest: expectedProcessBatchRequest, 749 batches: batches, 750 }, 751 { 752 name: "ProcessBatch Result Error", 753 processBatchErr: testErr, 754 internalErr: testErr, 755 expectedProcessRequest: expectedProcessBatchRequest, 756 expectedErr: testErr, 757 batches: batches, 758 expectedProcessBatchResult: &state.ProcessBatchResponse{ 759 IsBatchProcessed: false, 760 ExecutorError: testErr2, 761 }, 762 }, 763 } 764 765 for _, tc := range testCases { 766 t.Run(tc.name, func(t *testing.T) { 767 // arrange 768 f.processRequest = tc.expectedProcessRequest 769 dbManagerMock.On("GetLastNBatches", ctx, n).Return(tc.batches, tc.getLastNBatchesErr).Once() 770 if tc.getLastNBatchesErr == nil { 771 executorMock.Mock.On("ProcessBatch", ctx, f.processRequest, true).Return(tc.expectedProcessBatchResult, tc.processBatchErr).Once() 772 dbManagerMock.On("GetBatchByNumber", ctx, f.batch.batchNumber, nil).Return(tc.batches[0], nil).Once() 773 } 774 775 // act 776 err := f.reprocessBatch(ctx, f.batch.batchNumber) 777 778 // assert 779 if tc.expectedErr != nil { 780 assert.Error(t, err) 781 assert.EqualError(t, err, tc.expectedErr.Error()) 782 assert.ErrorIs(t, err, tc.internalErr) 783 } else { 784 assert.NoError(t, err) 785 } 786 }) 787 } 788 } 789 */ 790 791 func TestFinalizer_isDeadlineEncountered(t *testing.T) { 792 // arrange 793 f = setupFinalizer(true) 794 now = testNow 795 defer func() { 796 now = time.Now 797 }() 798 testCases := []struct { 799 name string 800 nextForcedBatch int64 801 nextGER int64 802 nextDelayedBatch int64 803 expected bool 804 }{ 805 { 806 name: "No deadlines", 807 nextForcedBatch: 0, 808 nextGER: 0, 809 nextDelayedBatch: 0, 810 expected: false, 811 }, 812 { 813 name: "Forced batch deadline", 814 nextForcedBatch: now().Add(time.Second).Unix(), 815 nextGER: 0, 816 nextDelayedBatch: 0, 817 expected: true, 818 }, 819 { 820 name: "Global Exit Root deadline", 821 nextForcedBatch: 0, 822 nextGER: now().Add(time.Second).Unix(), 823 nextDelayedBatch: 0, 824 expected: true, 825 }, 826 { 827 name: "Delayed batch deadline", 828 nextForcedBatch: 0, 829 nextGER: 0, 830 nextDelayedBatch: now().Add(time.Second).Unix(), 831 expected: false, 832 }, 833 } 834 835 for _, tc := range testCases { 836 t.Run(tc.name, func(t *testing.T) { 837 // arrange 838 f.nextForcedBatchDeadline = tc.nextForcedBatch 839 f.nextGERDeadline = tc.nextGER 840 if tc.expected == true { 841 now = func() time.Time { 842 return testNow().Add(time.Second * 2) 843 } 844 } 845 846 // act 847 actual := f.isDeadlineEncountered() 848 849 // assert 850 assert.Equal(t, tc.expected, actual) 851 }) 852 } 853 } 854 855 func TestFinalizer_checkRemainingResources(t *testing.T) { 856 // arrange 857 f = setupFinalizer(true) 858 ctx := context.Background() 859 txResponse := &state.ProcessTransactionResponse{TxHash: oldHash} 860 result := &state.ProcessBatchResponse{ 861 UsedZkCounters: state.ZKCounters{CumulativeGasUsed: 1000}, 862 Responses: []*state.ProcessTransactionResponse{txResponse}, 863 } 864 remainingResources := state.BatchResources{ 865 ZKCounters: state.ZKCounters{CumulativeGasUsed: 9000}, 866 Bytes: 10000, 867 } 868 f.batch.remainingResources = remainingResources 869 testCases := []struct { 870 name string 871 remaining state.BatchResources 872 expectedErr error 873 expectedWorkerUpdate bool 874 expectedTxTracker *TxTracker 875 }{ 876 { 877 name: "Success", 878 remaining: remainingResources, 879 expectedErr: nil, 880 expectedWorkerUpdate: false, 881 expectedTxTracker: &TxTracker{RawTx: []byte("test")}, 882 }, 883 { 884 name: "Bytes Resource Exceeded", 885 remaining: state.BatchResources{ 886 Bytes: 0, 887 }, 888 expectedErr: state.ErrBatchResourceBytesUnderflow, 889 expectedWorkerUpdate: true, 890 expectedTxTracker: &TxTracker{RawTx: []byte("test")}, 891 }, 892 { 893 name: "ZkCounter Resource Exceeded", 894 remaining: state.BatchResources{ 895 ZKCounters: state.ZKCounters{CumulativeGasUsed: 0}, 896 }, 897 expectedErr: state.NewBatchRemainingResourcesUnderflowError(cumulativeGasErr, cumulativeGasErr.Error()), 898 expectedWorkerUpdate: true, 899 expectedTxTracker: &TxTracker{RawTx: make([]byte, 0)}, 900 }, 901 } 902 903 for _, tc := range testCases { 904 t.Run(tc.name, func(t *testing.T) { 905 // arrange 906 f.batch.remainingResources = tc.remaining 907 dbManagerMock.On("AddEvent", ctx, mock.Anything, nil).Return(nil) 908 if tc.expectedWorkerUpdate { 909 workerMock.On("UpdateTx", txResponse.TxHash, tc.expectedTxTracker.From, result.UsedZkCounters).Return().Once() 910 } 911 912 // act 913 err := f.checkRemainingResources(result, tc.expectedTxTracker) 914 915 // assert 916 if tc.expectedErr != nil { 917 assert.Error(t, err) 918 assert.EqualError(t, err, tc.expectedErr.Error()) 919 } else { 920 assert.NoError(t, err) 921 } 922 if tc.expectedWorkerUpdate { 923 workerMock.AssertCalled(t, "UpdateTx", txResponse.TxHash, tc.expectedTxTracker.From, result.UsedZkCounters) 924 } else { 925 workerMock.AssertNotCalled(t, "UpdateTx", mock.Anything, mock.Anything, mock.Anything) 926 } 927 }) 928 } 929 } 930 931 func TestFinalizer_isBatchReadyToClose(t *testing.T) { 932 // arrange 933 f = setupFinalizer(true) 934 maxRemainingResource := getMaxRemainingResources(bc) 935 testCases := []struct { 936 name string 937 cumulativeGasUsed uint64 938 expectedResult bool 939 }{ 940 { 941 name: "Is ready", 942 cumulativeGasUsed: f.getConstraintThresholdUint64(bc.MaxCumulativeGasUsed) - 1, 943 expectedResult: true, 944 }, { 945 name: "Is NOT ready", 946 cumulativeGasUsed: f.getConstraintThresholdUint64(bc.MaxCumulativeGasUsed) + 1, 947 expectedResult: false, 948 }, 949 } 950 951 for _, tc := range testCases { 952 t.Run(tc.name, func(t *testing.T) { 953 maxRemainingResource.ZKCounters.CumulativeGasUsed = tc.cumulativeGasUsed 954 f.batch.remainingResources = maxRemainingResource 955 // act 956 result := f.isBatchAlmostFull() 957 958 // assert 959 assert.Equal(t, tc.expectedResult, result) 960 }) 961 } 962 } 963 964 func TestFinalizer_setNextForcedBatchDeadline(t *testing.T) { 965 // arrange 966 f = setupFinalizer(false) 967 now = testNow 968 defer func() { 969 now = time.Now 970 }() 971 expected := now().Unix() + int64(f.cfg.ForcedBatchDeadlineTimeout.Duration.Seconds()) 972 973 // act 974 f.setNextForcedBatchDeadline() 975 976 // assert 977 assert.Equal(t, expected, f.nextForcedBatchDeadline) 978 } 979 980 func TestFinalizer_setNextGERDeadline(t *testing.T) { 981 // arrange 982 f = setupFinalizer(false) 983 now = testNow 984 defer func() { 985 now = time.Now 986 }() 987 expected := now().Unix() + int64(f.cfg.GERDeadlineTimeout.Duration.Seconds()) 988 989 // act 990 f.setNextGERDeadline() 991 992 // assert 993 assert.Equal(t, expected, f.nextGERDeadline) 994 } 995 996 func TestFinalizer_getConstraintThresholdUint64(t *testing.T) { 997 // arrange 998 f = setupFinalizer(false) 999 input := uint64(100) 1000 expect := input * uint64(f.cfg.ResourcePercentageToCloseBatch) / 100 1001 1002 // act 1003 result := f.getConstraintThresholdUint64(input) 1004 1005 // assert 1006 assert.Equal(t, result, expect) 1007 } 1008 1009 func TestFinalizer_getConstraintThresholdUint32(t *testing.T) { 1010 // arrange 1011 f = setupFinalizer(false) 1012 input := uint32(100) 1013 expect := uint32(input * f.cfg.ResourcePercentageToCloseBatch / 100) 1014 1015 // act 1016 result := f.getConstraintThresholdUint32(input) 1017 1018 // assert 1019 assert.Equal(t, result, expect) 1020 } 1021 1022 func TestFinalizer_getRemainingResources(t *testing.T) { 1023 // act 1024 remainingResources := getMaxRemainingResources(bc) 1025 1026 // assert 1027 assert.Equal(t, remainingResources.ZKCounters.CumulativeGasUsed, bc.MaxCumulativeGasUsed) 1028 assert.Equal(t, remainingResources.ZKCounters.UsedKeccakHashes, bc.MaxKeccakHashes) 1029 assert.Equal(t, remainingResources.ZKCounters.UsedPoseidonHashes, bc.MaxPoseidonHashes) 1030 assert.Equal(t, remainingResources.ZKCounters.UsedPoseidonPaddings, bc.MaxPoseidonPaddings) 1031 assert.Equal(t, remainingResources.ZKCounters.UsedMemAligns, bc.MaxMemAligns) 1032 assert.Equal(t, remainingResources.ZKCounters.UsedArithmetics, bc.MaxArithmetics) 1033 assert.Equal(t, remainingResources.ZKCounters.UsedBinaries, bc.MaxBinaries) 1034 assert.Equal(t, remainingResources.ZKCounters.UsedSteps, bc.MaxSteps) 1035 assert.Equal(t, remainingResources.Bytes, bc.MaxBatchBytesSize) 1036 } 1037 1038 func setupFinalizer(withWipBatch bool) *finalizer { 1039 wipBatch := new(WipBatch) 1040 dbManagerMock = new(DbManagerMock) 1041 executorMock = new(StateMock) 1042 workerMock = new(WorkerMock) 1043 dbTxMock = new(DbTxMock) 1044 if withWipBatch { 1045 wipBatch = &WipBatch{ 1046 batchNumber: 1, 1047 coinbase: seqAddr, 1048 initialStateRoot: oldHash, 1049 stateRoot: newHash, 1050 timestamp: now(), 1051 globalExitRoot: oldHash, 1052 remainingResources: getMaxRemainingResources(bc), 1053 } 1054 } 1055 return &finalizer{ 1056 cfg: cfg, 1057 txsStore: txsStore, 1058 closingSignalCh: closingSignalCh, 1059 isSynced: isSynced, 1060 sequencerAddress: seqAddr, 1061 worker: workerMock, 1062 dbManager: dbManagerMock, 1063 executor: executorMock, 1064 sharedResourcesMux: new(sync.RWMutex), 1065 batch: wipBatch, 1066 batchConstraints: bc, 1067 processRequest: state.ProcessRequest{}, 1068 // closing signals 1069 nextGER: common.Hash{}, 1070 nextGERDeadline: 0, 1071 nextGERMux: new(sync.RWMutex), 1072 nextForcedBatches: make([]state.ForcedBatch, 0), 1073 nextForcedBatchDeadline: 0, 1074 nextForcedBatchesMux: new(sync.RWMutex), 1075 } 1076 } 1077 1078 func TestFinalizer_handleTransactionError(t *testing.T) { 1079 // arrange 1080 f = setupFinalizer(true) 1081 nonce := uint64(0) 1082 tx := &TxTracker{Hash: oldHash, From: sender, Cost: big.NewInt(0)} 1083 testCases := []struct { 1084 name string 1085 error pb.RomError 1086 expectedDeleteCall bool 1087 updateTxStatus pool.TxStatus 1088 expectedMoveCall bool 1089 }{ 1090 { 1091 name: "OutOfCountersError", 1092 error: pb.RomError(executor.ROM_ERROR_OUT_OF_COUNTERS_STEP), 1093 updateTxStatus: pool.TxStatusInvalid, 1094 expectedDeleteCall: true, 1095 }, 1096 { 1097 name: "IntrinsicError", 1098 error: pb.RomError(executor.ROM_ERROR_INTRINSIC_INVALID_NONCE), 1099 updateTxStatus: pool.TxStatusFailed, 1100 expectedMoveCall: true, 1101 }, 1102 } 1103 for _, tc := range testCases { 1104 t.Run(tc.name, func(t *testing.T) { 1105 // arrange 1106 if tc.expectedDeleteCall { 1107 workerMock.On("DeleteTx", oldHash, sender).Return() 1108 dbManagerMock.On("UpdateTxStatus", ctx, oldHash, tc.updateTxStatus, false, mock.Anything).Return(nil).Once() 1109 dbManagerMock.On("DeleteTransactionFromPool", ctx, tx.Hash).Return(nil).Once() 1110 } 1111 if tc.expectedMoveCall { 1112 workerMock.On("MoveTxToNotReady", oldHash, sender, &nonce, big.NewInt(0)).Return([]*TxTracker{}).Once() 1113 } 1114 1115 result := &state.ProcessBatchResponse{ 1116 ReadWriteAddresses: map[common.Address]*state.InfoReadWrite{ 1117 sender: {Nonce: &nonce, Balance: big.NewInt(0)}, 1118 }, 1119 Responses: []*state.ProcessTransactionResponse{{ 1120 RomError: executor.RomErr(tc.error), 1121 }, 1122 }, 1123 } 1124 1125 // act 1126 wg := f.handleProcessTransactionError(ctx, result, tx) 1127 if wg != nil { 1128 wg.Wait() 1129 } 1130 1131 // assert 1132 workerMock.AssertExpectations(t) 1133 }) 1134 } 1135 }