github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/sequencer/dbmanager.go (about) 1 package sequencer 2 3 import ( 4 "context" 5 "math/big" 6 "time" 7 8 "github.com/0xPolygon/supernets2-node/log" 9 "github.com/0xPolygon/supernets2-node/pool" 10 "github.com/0xPolygon/supernets2-node/state" 11 "github.com/ethereum/go-ethereum/common" 12 "github.com/ethereum/go-ethereum/core/types" 13 "github.com/jackc/pgx/v4" 14 ) 15 16 // Pool Loader and DB Updater 17 type dbManager struct { 18 cfg DBManagerCfg 19 txPool txPool 20 state stateInterface 21 worker workerInterface 22 txsStore TxsStore 23 l2ReorgCh chan L2ReorgEvent 24 ctx context.Context 25 batchConstraints batchConstraints 26 numberOfReorgs uint64 27 } 28 29 func (d *dbManager) GetBatchByNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error) { 30 return d.state.GetBatchByNumber(ctx, batchNumber, dbTx) 31 } 32 33 // ClosingBatchParameters contains the necessary parameters to close a batch 34 type ClosingBatchParameters struct { 35 BatchNumber uint64 36 StateRoot common.Hash 37 LocalExitRoot common.Hash 38 AccInputHash common.Hash 39 Txs []types.Transaction 40 BatchResources state.BatchResources 41 ClosingReason state.ClosingReason 42 } 43 44 func newDBManager(ctx context.Context, config DBManagerCfg, txPool txPool, state stateInterface, worker *Worker, closingSignalCh ClosingSignalCh, txsStore TxsStore, batchConstraints batchConstraints) *dbManager { 45 numberOfReorgs, err := state.CountReorgs(ctx, nil) 46 if err != nil { 47 log.Error("failed to get number of reorgs: %v", err) 48 } 49 50 return &dbManager{ctx: ctx, cfg: config, txPool: txPool, state: state, worker: worker, txsStore: txsStore, l2ReorgCh: closingSignalCh.L2ReorgCh, batchConstraints: batchConstraints, numberOfReorgs: numberOfReorgs} 51 } 52 53 // Start stars the dbManager routines 54 func (d *dbManager) Start() { 55 go d.loadFromPool() 56 go func() { 57 for { 58 time.Sleep(d.cfg.L2ReorgRetrievalInterval.Duration) 59 d.checkIfReorg() 60 } 61 }() 62 go d.storeProcessedTxAndDeleteFromPool() 63 } 64 65 // GetLastBatchNumber get the latest batch number from state 66 func (d *dbManager) GetLastBatchNumber(ctx context.Context) (uint64, error) { 67 return d.state.GetLastBatchNumber(ctx, nil) 68 } 69 70 // OpenBatch opens a new batch to star processing transactions 71 func (d *dbManager) OpenBatch(ctx context.Context, processingContext state.ProcessingContext, dbTx pgx.Tx) error { 72 return d.state.OpenBatch(ctx, processingContext, dbTx) 73 } 74 75 // CreateFirstBatch is using during genesis 76 func (d *dbManager) CreateFirstBatch(ctx context.Context, sequencerAddress common.Address) state.ProcessingContext { 77 processingCtx := state.ProcessingContext{ 78 BatchNumber: 1, 79 Coinbase: sequencerAddress, 80 Timestamp: time.Now(), 81 GlobalExitRoot: state.ZeroHash, 82 } 83 dbTx, err := d.state.BeginStateTransaction(ctx) 84 if err != nil { 85 log.Errorf("failed to begin state transaction for opening a batch, err: %v", err) 86 return processingCtx 87 } 88 err = d.state.OpenBatch(ctx, processingCtx, dbTx) 89 if err != nil { 90 if rollbackErr := dbTx.Rollback(ctx); rollbackErr != nil { 91 log.Errorf( 92 "failed to rollback dbTx when opening batch that gave err: %v. Rollback err: %v", 93 rollbackErr, err, 94 ) 95 } 96 log.Errorf("failed to open a batch, err: %v", err) 97 return processingCtx 98 } 99 if err := dbTx.Commit(ctx); err != nil { 100 log.Errorf("failed to commit dbTx when opening batch, err: %v", err) 101 return processingCtx 102 } 103 return processingCtx 104 } 105 106 // checkIfReorg checks if a reorg has happened 107 func (d *dbManager) checkIfReorg() { 108 numberOfReorgs, err := d.state.CountReorgs(d.ctx, nil) 109 if err != nil { 110 log.Error("failed to get number of reorgs: %v", err) 111 } 112 113 if numberOfReorgs != d.numberOfReorgs { 114 log.Warnf("New L2 reorg detected") 115 d.l2ReorgCh <- L2ReorgEvent{} 116 } 117 } 118 119 // loadFromPool keeps loading transactions from the pool 120 func (d *dbManager) loadFromPool() { 121 for { 122 time.Sleep(d.cfg.PoolRetrievalInterval.Duration) 123 124 poolTransactions, err := d.txPool.GetNonWIPPendingTxs(d.ctx, 0) 125 if err != nil && err != pool.ErrNotFound { 126 log.Errorf("load tx from pool: %v", err) 127 } 128 129 for _, tx := range poolTransactions { 130 err := d.addTxToWorker(tx) 131 if err != nil { 132 log.Errorf("error adding transaction to worker: %v", err) 133 } 134 } 135 } 136 } 137 138 func (d *dbManager) addTxToWorker(tx pool.Transaction) error { 139 txTracker, err := d.worker.NewTxTracker(tx.Transaction, tx.ZKCounters, tx.IP) 140 if err != nil { 141 return err 142 } 143 dropReason, isWIP := d.worker.AddTxTracker(d.ctx, txTracker) 144 if dropReason != nil { 145 failedReason := dropReason.Error() 146 return d.txPool.UpdateTxStatus(d.ctx, txTracker.Hash, pool.TxStatusFailed, false, &failedReason) 147 } else { 148 if isWIP { 149 return d.txPool.UpdateTxWIPStatus(d.ctx, tx.Hash(), true) 150 } 151 } 152 153 return nil 154 } 155 156 // BeginStateTransaction starts a db transaction in the state 157 func (d *dbManager) BeginStateTransaction(ctx context.Context) (pgx.Tx, error) { 158 return d.state.BeginStateTransaction(ctx) 159 } 160 161 // StoreProcessedTransaction stores a transaction in the state 162 func (d *dbManager) StoreProcessedTransaction(ctx context.Context, batchNumber uint64, processedTx *state.ProcessTransactionResponse, coinbase common.Address, timestamp uint64, dbTx pgx.Tx) error { 163 return d.state.StoreTransaction(ctx, batchNumber, processedTx, coinbase, timestamp, dbTx) 164 } 165 166 // DeleteTransactionFromPool deletes a transaction from the pool 167 func (d *dbManager) DeleteTransactionFromPool(ctx context.Context, txHash common.Hash) error { 168 return d.txPool.DeleteTransactionByHash(ctx, txHash) 169 } 170 171 // storeProcessedTxAndDeleteFromPool stores a tx into the state and changes it status in the pool 172 func (d *dbManager) storeProcessedTxAndDeleteFromPool() { 173 for { 174 txToStore := <-d.txsStore.Ch 175 d.checkIfReorg() 176 177 // Flush the state db 178 err := d.state.FlushMerkleTree(d.ctx) 179 if err != nil { 180 log.Fatalf("StoreProcessedTxAndDeleteFromPool. Error flushing state db: %v", err) 181 } 182 183 log.Debugf("Storing tx %v", txToStore.txResponse.TxHash) 184 dbTx, err := d.BeginStateTransaction(d.ctx) 185 if err != nil { 186 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 187 } 188 189 err = d.StoreProcessedTransaction(d.ctx, txToStore.batchNumber, txToStore.txResponse, txToStore.coinbase, txToStore.timestamp, dbTx) 190 if err != nil { 191 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 192 } 193 194 // Update batch l2 data 195 batch, err := d.state.GetBatchByNumber(d.ctx, txToStore.batchNumber, dbTx) 196 if err != nil { 197 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 198 } 199 200 txData, err := state.EncodeTransaction(txToStore.txResponse.Tx) 201 if err != nil { 202 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 203 } 204 batch.BatchL2Data = append(batch.BatchL2Data, txData...) 205 206 if !txToStore.isForcedBatch { 207 err = d.state.UpdateBatchL2Data(d.ctx, txToStore.batchNumber, batch.BatchL2Data, dbTx) 208 if err != nil { 209 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 210 } 211 } 212 213 err = dbTx.Commit(d.ctx) 214 if err != nil { 215 log.Fatalf("StoreProcessedTxAndDeleteFromPool error committing : %v", err) 216 } 217 218 // Change Tx status to selected 219 err = d.txPool.UpdateTxStatus(d.ctx, txToStore.txResponse.TxHash, pool.TxStatusSelected, false, nil) 220 if err != nil { 221 log.Fatalf("StoreProcessedTxAndDeleteFromPool: %v", err) 222 } 223 224 log.Infof("StoreProcessedTxAndDeleteFromPool: successfully stored tx: %v for batch: %v", txToStore.txResponse.TxHash.String(), txToStore.batchNumber) 225 d.txsStore.Wg.Done() 226 } 227 } 228 229 // GetWIPBatch returns ready WIP batch 230 func (d *dbManager) GetWIPBatch(ctx context.Context) (*WipBatch, error) { 231 const two = 2 232 var lastBatch, previousLastBatch *state.Batch 233 dbTx, err := d.BeginStateTransaction(ctx) 234 if err != nil { 235 return nil, err 236 } 237 defer func() { 238 err := dbTx.Commit(ctx) 239 if err != nil { 240 log.Errorf("failed to commit GetWIPBatch: %v", err) 241 } 242 }() 243 244 lastBatches, err := d.state.GetLastNBatches(ctx, two, dbTx) 245 if err != nil { 246 return nil, err 247 } 248 249 lastBatch = lastBatches[0] 250 if len(lastBatches) > 1 { 251 previousLastBatch = lastBatches[1] 252 } 253 254 lastBatchTxs, _, err := state.DecodeTxs(lastBatch.BatchL2Data) 255 if err != nil { 256 return nil, err 257 } 258 lastBatch.Transactions = lastBatchTxs 259 260 var prevLastBatchTxs []types.Transaction 261 if previousLastBatch != nil { 262 prevLastBatchTxs, _, err = state.DecodeTxs(previousLastBatch.BatchL2Data) 263 if err != nil { 264 return nil, err 265 } 266 } 267 268 var lastStateRoot common.Hash 269 // If the last two batches have no txs, the stateRoot can not be retrieved from the l2block because there is no tx. 270 // In this case, the stateRoot must be gotten from the previousLastBatch 271 if len(lastBatchTxs) == 0 && previousLastBatch != nil && len(prevLastBatchTxs) == 0 { 272 lastStateRoot = previousLastBatch.StateRoot 273 } else { 274 lastStateRoot, err = d.state.GetLastStateRoot(ctx, dbTx) 275 if err != nil { 276 return nil, err 277 } 278 } 279 280 wipBatch := &WipBatch{ 281 batchNumber: lastBatch.BatchNumber, 282 coinbase: lastBatch.Coinbase, 283 localExitRoot: lastBatch.LocalExitRoot, 284 timestamp: lastBatch.Timestamp, 285 globalExitRoot: lastBatch.GlobalExitRoot, 286 countOfTxs: len(lastBatch.Transactions), 287 } 288 289 // Init counters to MAX values 290 var totalBytes uint64 = d.batchConstraints.MaxBatchBytesSize 291 var batchZkCounters state.ZKCounters = state.ZKCounters{ 292 CumulativeGasUsed: d.batchConstraints.MaxCumulativeGasUsed, 293 UsedKeccakHashes: d.batchConstraints.MaxKeccakHashes, 294 UsedPoseidonHashes: d.batchConstraints.MaxPoseidonHashes, 295 UsedPoseidonPaddings: d.batchConstraints.MaxPoseidonPaddings, 296 UsedMemAligns: d.batchConstraints.MaxMemAligns, 297 UsedArithmetics: d.batchConstraints.MaxArithmetics, 298 UsedBinaries: d.batchConstraints.MaxBinaries, 299 UsedSteps: d.batchConstraints.MaxSteps, 300 } 301 302 isClosed, err := d.IsBatchClosed(ctx, lastBatch.BatchNumber) 303 if err != nil { 304 return nil, err 305 } 306 307 if isClosed { 308 wipBatch.batchNumber = lastBatch.BatchNumber + 1 309 wipBatch.stateRoot = lastBatch.StateRoot 310 wipBatch.initialStateRoot = lastBatch.StateRoot 311 312 processingContext := &state.ProcessingContext{ 313 BatchNumber: wipBatch.batchNumber, 314 Coinbase: wipBatch.coinbase, 315 Timestamp: wipBatch.timestamp, 316 GlobalExitRoot: wipBatch.globalExitRoot, 317 } 318 err = d.state.OpenBatch(ctx, *processingContext, dbTx) 319 if err != nil { 320 if rollbackErr := dbTx.Rollback(ctx); rollbackErr != nil { 321 log.Errorf( 322 "failed to rollback dbTx when opening batch that gave err: %v. Rollback err: %v", 323 rollbackErr, err, 324 ) 325 } 326 log.Errorf("failed to open a batch, err: %v", err) 327 return nil, err 328 } 329 if err := dbTx.Commit(ctx); err != nil { 330 log.Errorf("failed to commit dbTx when opening batch, err: %v", err) 331 return nil, err 332 } 333 } else { 334 wipBatch.stateRoot = lastStateRoot 335 wipBatch.initialStateRoot = previousLastBatch.StateRoot 336 batchL2DataLen := len(lastBatch.BatchL2Data) 337 338 if batchL2DataLen > 0 { 339 wipBatch.countOfTxs = len(lastBatch.Transactions) 340 batchToExecute := *lastBatch 341 batchToExecute.BatchNumber = wipBatch.batchNumber 342 batchResponse, err := d.state.ExecuteBatch(ctx, batchToExecute, false, dbTx) 343 if err != nil { 344 return nil, err 345 } 346 347 zkCounters := &state.ZKCounters{ 348 CumulativeGasUsed: batchResponse.GetCumulativeGasUsed(), 349 UsedKeccakHashes: batchResponse.CntKeccakHashes, 350 UsedPoseidonHashes: batchResponse.CntPoseidonHashes, 351 UsedPoseidonPaddings: batchResponse.CntPoseidonPaddings, 352 UsedMemAligns: batchResponse.CntMemAligns, 353 UsedArithmetics: batchResponse.CntArithmetics, 354 UsedBinaries: batchResponse.CntBinaries, 355 UsedSteps: batchResponse.CntSteps, 356 } 357 358 err = batchZkCounters.Sub(*zkCounters) 359 if err != nil { 360 return nil, err 361 } 362 363 totalBytes -= uint64(batchL2DataLen) 364 } else { 365 wipBatch.countOfTxs = 0 366 } 367 } 368 369 wipBatch.remainingResources = state.BatchResources{ZKCounters: batchZkCounters, Bytes: totalBytes} 370 return wipBatch, nil 371 } 372 373 // GetLastClosedBatch gets the latest closed batch from state 374 func (d *dbManager) GetLastClosedBatch(ctx context.Context) (*state.Batch, error) { 375 return d.state.GetLastClosedBatch(ctx, nil) 376 } 377 378 // GetLastBatch gets the latest batch from state 379 func (d *dbManager) GetLastBatch(ctx context.Context) (*state.Batch, error) { 380 batch, err := d.state.GetLastBatch(d.ctx, nil) 381 if err != nil { 382 return nil, err 383 } 384 return batch, nil 385 } 386 387 // IsBatchClosed checks if a batch is closed 388 func (d *dbManager) IsBatchClosed(ctx context.Context, batchNum uint64) (bool, error) { 389 return d.state.IsBatchClosed(ctx, batchNum, nil) 390 } 391 392 // GetLastNBatches gets the latest N batches from state 393 func (d *dbManager) GetLastNBatches(ctx context.Context, numBatches uint) ([]*state.Batch, error) { 394 return d.state.GetLastNBatches(ctx, numBatches, nil) 395 } 396 397 // GetLatestGer gets the latest global exit root 398 func (d *dbManager) GetLatestGer(ctx context.Context, gerFinalityNumberOfBlocks uint64) (state.GlobalExitRoot, time.Time, error) { 399 return d.state.GetLatestGer(ctx, gerFinalityNumberOfBlocks) 400 } 401 402 // CloseBatch closes a batch in the state 403 func (d *dbManager) CloseBatch(ctx context.Context, params ClosingBatchParameters) error { 404 processingReceipt := state.ProcessingReceipt{ 405 BatchNumber: params.BatchNumber, 406 StateRoot: params.StateRoot, 407 LocalExitRoot: params.LocalExitRoot, 408 AccInputHash: params.AccInputHash, 409 BatchResources: params.BatchResources, 410 ClosingReason: params.ClosingReason, 411 } 412 413 batchL2Data, err := state.EncodeTransactions(params.Txs) 414 if err != nil { 415 return err 416 } 417 418 processingReceipt.BatchL2Data = batchL2Data 419 420 dbTx, err := d.BeginStateTransaction(ctx) 421 if err != nil { 422 return err 423 } 424 425 err = d.state.CloseBatch(ctx, processingReceipt, dbTx) 426 if err != nil { 427 err2 := dbTx.Rollback(ctx) 428 if err2 != nil { 429 log.Errorf("CloseBatch error rolling back: %v", err2) 430 } 431 return err 432 } else { 433 err := dbTx.Commit(ctx) 434 if err != nil { 435 log.Errorf("CloseBatch error committing: %v", err) 436 return err 437 } 438 } 439 440 return nil 441 } 442 443 // ProcessForcedBatch process a forced batch 444 func (d *dbManager) ProcessForcedBatch(ForcedBatchNumber uint64, request state.ProcessRequest) (*state.ProcessBatchResponse, error) { 445 // Open Batch 446 processingCtx := state.ProcessingContext{ 447 BatchNumber: request.BatchNumber, 448 Coinbase: request.Coinbase, 449 Timestamp: request.Timestamp, 450 GlobalExitRoot: request.GlobalExitRoot, 451 ForcedBatchNum: &ForcedBatchNumber, 452 } 453 dbTx, err := d.state.BeginStateTransaction(d.ctx) 454 if err != nil { 455 log.Errorf("failed to begin state transaction for opening a forced batch, err: %v", err) 456 return nil, err 457 } 458 459 err = d.state.OpenBatch(d.ctx, processingCtx, dbTx) 460 if err != nil { 461 if rollbackErr := dbTx.Rollback(d.ctx); rollbackErr != nil { 462 log.Errorf( 463 "failed to rollback dbTx when opening a forced batch that gave err: %v. Rollback err: %v", 464 rollbackErr, err, 465 ) 466 } 467 log.Errorf("failed to open a batch, err: %v", err) 468 return nil, err 469 } 470 471 // Fetch Forced Batch 472 forcedBatch, err := d.state.GetForcedBatch(d.ctx, ForcedBatchNumber, dbTx) 473 if err != nil { 474 if rollbackErr := dbTx.Rollback(d.ctx); rollbackErr != nil { 475 log.Errorf( 476 "failed to rollback dbTx when getting forced batch err: %v. Rollback err: %v", 477 rollbackErr, err, 478 ) 479 } 480 log.Errorf("failed to get a forced batch, err: %v", err) 481 return nil, err 482 } 483 484 // Process Batch 485 processBatchResponse, err := d.state.ProcessSequencerBatch(d.ctx, request.BatchNumber, forcedBatch.RawTxsData, request.Caller, dbTx) 486 if err != nil { 487 log.Errorf("failed to process a forced batch, err: %v", err) 488 return nil, err 489 } 490 491 // Close Batch 492 txsBytes := uint64(0) 493 for _, resp := range processBatchResponse.Responses { 494 if !resp.ChangesStateRoot { 495 continue 496 } 497 txsBytes += resp.Tx.Size() 498 } 499 processingReceipt := state.ProcessingReceipt{ 500 BatchNumber: request.BatchNumber, 501 StateRoot: processBatchResponse.NewStateRoot, 502 LocalExitRoot: processBatchResponse.NewLocalExitRoot, 503 AccInputHash: processBatchResponse.NewAccInputHash, 504 BatchL2Data: forcedBatch.RawTxsData, 505 BatchResources: state.BatchResources{ 506 ZKCounters: processBatchResponse.UsedZkCounters, 507 Bytes: txsBytes, 508 }, 509 ClosingReason: state.ForcedBatchClosingReason, 510 } 511 512 isClosed := false 513 tryToCloseAndCommit := true 514 for tryToCloseAndCommit { 515 if !isClosed { 516 closingErr := d.state.CloseBatch(d.ctx, processingReceipt, dbTx) 517 tryToCloseAndCommit = closingErr != nil 518 if tryToCloseAndCommit { 519 continue 520 } 521 isClosed = true 522 } 523 524 if err := dbTx.Commit(d.ctx); err != nil { 525 log.Errorf("failed to commit dbTx when processing a forced batch, err: %v", err) 526 } 527 tryToCloseAndCommit = err != nil 528 } 529 530 return processBatchResponse, nil 531 } 532 533 // GetForcedBatchesSince gets L1 forced batches since timestamp 534 func (d *dbManager) GetForcedBatchesSince(ctx context.Context, forcedBatchNumber, maxBlockNumber uint64, dbTx pgx.Tx) ([]*state.ForcedBatch, error) { 535 return d.state.GetForcedBatchesSince(ctx, forcedBatchNumber, maxBlockNumber, dbTx) 536 } 537 538 // GetLastL2BlockHeader gets the last l2 block number 539 func (d *dbManager) GetLastL2BlockHeader(ctx context.Context, dbTx pgx.Tx) (*types.Header, error) { 540 return d.state.GetLastL2BlockHeader(ctx, dbTx) 541 } 542 543 func (d *dbManager) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*state.Block, error) { 544 return d.state.GetLastBlock(ctx, dbTx) 545 } 546 547 func (d *dbManager) GetLastTrustedForcedBatchNumber(ctx context.Context, dbTx pgx.Tx) (uint64, error) { 548 return d.state.GetLastTrustedForcedBatchNumber(ctx, dbTx) 549 } 550 551 func (d *dbManager) GetBalanceByStateRoot(ctx context.Context, address common.Address, root common.Hash) (*big.Int, error) { 552 return d.state.GetBalanceByStateRoot(ctx, address, root) 553 } 554 555 func (d *dbManager) GetTransactionsByBatchNumber(ctx context.Context, batchNumber uint64) (txs []types.Transaction, err error) { 556 return d.state.GetTransactionsByBatchNumber(ctx, batchNumber, nil) 557 } 558 559 func (d *dbManager) UpdateTxStatus(ctx context.Context, hash common.Hash, newStatus pool.TxStatus, isWIP bool, failedReason *string) error { 560 return d.txPool.UpdateTxStatus(ctx, hash, newStatus, isWIP, failedReason) 561 } 562 563 // GetLatestVirtualBatchTimestamp gets last virtual batch timestamp 564 func (d *dbManager) GetLatestVirtualBatchTimestamp(ctx context.Context, dbTx pgx.Tx) (time.Time, error) { 565 return d.state.GetLatestVirtualBatchTimestamp(ctx, dbTx) 566 } 567 568 // CountReorgs returns the number of reorgs 569 func (d *dbManager) CountReorgs(ctx context.Context, dbTx pgx.Tx) (uint64, error) { 570 return d.state.CountReorgs(ctx, dbTx) 571 } 572 573 // FlushMerkleTree persists updates in the Merkle tree 574 func (d *dbManager) FlushMerkleTree(ctx context.Context) error { 575 return d.state.FlushMerkleTree(ctx) 576 }