From d71f4e2eb4af45a2d676f799079e7a14503b4604 Mon Sep 17 00:00:00 2001 From: Michael Tsitrin <114929630+mtsitrin@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:38:57 +0300 Subject: [PATCH] fix(manager): re-use submitted DA batches on SL failure (#708) --- block/manager.go | 10 ++++++-- block/submit.go | 59 +++++++++++++++++++++++++++++++++++++++--------- block/types.go | 10 ++++++++ 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/block/manager.go b/block/manager.go index 8dada30b9..f30de5925 100644 --- a/block/manager.go +++ b/block/manager.go @@ -60,11 +60,17 @@ type Manager struct { // Block production shouldProduceBlocksCh chan bool produceEmptyBlockCh chan bool - lastSubmissionTime atomic.Int64 - batchInProcess sync.Mutex produceBlockMutex sync.Mutex applyCachedBlockMutex sync.Mutex + // batch submission + batchInProcess sync.Mutex + lastSubmissionTime atomic.Int64 + // pendingBatch is the result of the last DA submission + // that is pending settlement layer submission. + // It is used to avoid double submission of the same batch. + pendingBatch *PendingBatch + // Logging logger types.Logger diff --git a/block/submit.go b/block/submit.go index dce22d41b..4ac1741c1 100644 --- a/block/submit.go +++ b/block/submit.go @@ -49,9 +49,30 @@ func (m *Manager) handleSubmissionTrigger(ctx context.Context) { m.logger.Error("produce empty block", "error", err) } - syncHeight, err := m.submitNextBatch() + if m.pendingBatch != nil { + m.logger.Info("pending batch exists", "startHeight", m.pendingBatch.batch.StartHeight, "endHeight", m.pendingBatch.batch.EndHeight) + } else { + nextBatch, err := m.createNextBatch() + if err != nil { + m.logger.Error("get next batch", "error", err) + return + } + + resultSubmitToDA, err := m.submitNextBatchToDA(nextBatch) + if err != nil { + m.logger.Error("submit next batch", "error", err) + return + } + + m.pendingBatch = &PendingBatch{ + daResult: resultSubmitToDA, + batch: nextBatch, + } + } + + syncHeight, err := m.submitPendingBatchToSL() if err != nil { - m.logger.Error("submit next batch", "error", err) + m.logger.Error("submit next batch to SL", "error", err) return } @@ -59,27 +80,31 @@ func (m *Manager) handleSubmissionTrigger(ctx context.Context) { m.updateSyncParams(syncHeight) } -func (m *Manager) submitNextBatch() (uint64, error) { - // Get the batch start and end height +func (m *Manager) createNextBatch() (*types.Batch, error) { + // Create the batch startHeight := m.syncTarget.Load() + 1 endHeight := m.store.Height() - - // Create the batch nextBatch, err := m.createNextDABatch(startHeight, endHeight) if err != nil { m.logger.Error("create next batch", "startHeight", startHeight, "endHeight", endHeight, "error", err) - return 0, err + return nil, err } if err := m.validateBatch(nextBatch); err != nil { - return 0, err + return nil, err } + + return nextBatch, nil +} + +func (m *Manager) submitNextBatchToDA(nextBatch *types.Batch) (*da.ResultSubmitBatch, error) { + startHeight := nextBatch.StartHeight actualEndHeight := nextBatch.EndHeight isLastBlockEmpty, err := m.isBlockEmpty(actualEndHeight) if err != nil { m.logger.Error("validate last block in batch is empty", "startHeight", startHeight, "endHeight", actualEndHeight, "error", err) - return 0, err + return nil, err } // Verify the last block in the batch is an empty block and that no ibc messages has accidentially passed through. // This block may not be empty if another block has passed it in line. If that's the case our empty block request will @@ -94,16 +119,28 @@ func (m *Manager) submitNextBatch() (uint64, error) { resultSubmitToDA := m.dalc.SubmitBatch(nextBatch) if resultSubmitToDA.Code != da.StatusSuccess { err = fmt.Errorf("submit next batch to DA Layer: %s", resultSubmitToDA.Message) - return 0, err + return nil, err + } + return &resultSubmitToDA, nil +} + +func (m *Manager) submitPendingBatchToSL() (uint64, error) { + if m.pendingBatch == nil { + return 0, fmt.Errorf("no pending batch to submit") } // Submit batch to SL - err = m.settlementClient.SubmitBatch(nextBatch, m.dalc.GetClientType(), &resultSubmitToDA) + startHeight := m.pendingBatch.batch.StartHeight + actualEndHeight := m.pendingBatch.batch.EndHeight + err := m.settlementClient.SubmitBatch(m.pendingBatch.batch, m.dalc.GetClientType(), m.pendingBatch.daResult) if err != nil { m.logger.Error("submit batch to SL", "startHeight", startHeight, "endHeight", actualEndHeight, "error", err) return 0, err } + // Clear pending batch + m.pendingBatch = nil + return actualEndHeight, nil } diff --git a/block/types.go b/block/types.go index b2519c183..22573d46a 100644 --- a/block/types.go +++ b/block/types.go @@ -1,5 +1,10 @@ package block +import ( + "github.com/dymensionxyz/dymint/da" + "github.com/dymensionxyz/dymint/types" +) + // TODO: move to types package type blockSource string @@ -13,3 +18,8 @@ type blockMetaData struct { source blockSource daHeight uint64 } + +type PendingBatch struct { + daResult *da.ResultSubmitBatch + batch *types.Batch +}