Skip to content

Commit b3dd979

Browse files
authored
Merge pull request #3051 from OffchainLabs/reorg_finality_data
Handle reorg issues when setting finality data in Execution
2 parents 18f9bee + f061399 commit b3dd979

File tree

10 files changed

+564
-313
lines changed

10 files changed

+564
-313
lines changed

arbnode/consensus_execution_syncer.go

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,22 @@ type ConsensusExecutionSyncer struct {
3939
inboxReader *InboxReader
4040
execClient execution.ExecutionClient
4141
blockValidator *staker.BlockValidator
42+
txStreamer *TransactionStreamer
4243
}
4344

4445
func NewConsensusExecutionSyncer(
4546
config func() *ConsensusExecutionSyncerConfig,
4647
inboxReader *InboxReader,
4748
execClient execution.ExecutionClient,
4849
blockValidator *staker.BlockValidator,
50+
txStreamer *TransactionStreamer,
4951
) *ConsensusExecutionSyncer {
5052
return &ConsensusExecutionSyncer{
5153
config: config,
5254
inboxReader: inboxReader,
5355
execClient: execClient,
5456
blockValidator: blockValidator,
57+
txStreamer: txStreamer,
5558
}
5659
}
5760

@@ -60,37 +63,61 @@ func (c *ConsensusExecutionSyncer) Start(ctx_in context.Context) {
6063
c.CallIteratively(c.pushFinalityDataFromConsensusToExecution)
6164
}
6265

66+
func (c *ConsensusExecutionSyncer) getFinalityData(
67+
ctx context.Context,
68+
msgCount arbutil.MessageIndex,
69+
errMsgCount error,
70+
scenario string,
71+
) (*arbutil.FinalityData, error) {
72+
if errors.Is(errMsgCount, headerreader.ErrBlockNumberNotSupported) {
73+
log.Debug("Finality not supported, not pushing finality data to execution")
74+
return nil, errMsgCount
75+
} else if errMsgCount != nil {
76+
log.Error("Error getting finality msg count", "scenario", scenario, "err", errMsgCount)
77+
return nil, errMsgCount
78+
}
79+
80+
if msgCount == 0 {
81+
return nil, nil
82+
}
83+
msgIdx := msgCount - 1
84+
msgResult, err := c.txStreamer.ResultAtMessageIndex(msgIdx)
85+
if err != nil {
86+
log.Error("Error getting message result", "msgIdx", msgIdx, "err", err)
87+
return nil, err
88+
}
89+
90+
finalityData := &arbutil.FinalityData{
91+
MsgIdx: msgIdx,
92+
BlockHash: msgResult.BlockHash,
93+
}
94+
return finalityData, nil
95+
}
96+
6397
func (c *ConsensusExecutionSyncer) pushFinalityDataFromConsensusToExecution(ctx context.Context) time.Duration {
6498
safeMsgCount, err := c.inboxReader.GetSafeMsgCount(ctx)
65-
if errors.Is(err, headerreader.ErrBlockNumberNotSupported) {
66-
log.Info("Finality not supported, not pushing finality data to execution")
67-
return c.config().SyncInterval
68-
} else if err != nil {
69-
log.Error("Error getting safe message count", "err", err)
99+
safeFinalityData, err := c.getFinalityData(ctx, safeMsgCount, err, "safe")
100+
if err != nil {
70101
return c.config().SyncInterval
71102
}
72103

73104
finalizedMsgCount, err := c.inboxReader.GetFinalizedMsgCount(ctx)
74-
if errors.Is(err, headerreader.ErrBlockNumberNotSupported) {
75-
log.Info("Finality not supported, not pushing finality data to execution")
76-
return c.config().SyncInterval
77-
} else if err != nil {
78-
log.Error("Error getting finalized message count", "err", err)
105+
finalizedFinalityData, err := c.getFinalityData(ctx, finalizedMsgCount, err, "finalized")
106+
if err != nil {
79107
return c.config().SyncInterval
80108
}
81109

110+
var validatedFinalityData *arbutil.FinalityData
82111
var validatedMsgCount arbutil.MessageIndex
83112
if c.blockValidator != nil {
84113
validatedMsgCount = c.blockValidator.GetValidated()
114+
validatedFinalityData, err = c.getFinalityData(ctx, validatedMsgCount, nil, "validated")
115+
if err != nil {
116+
return c.config().SyncInterval
117+
}
85118
}
86119

87-
finalityData := &arbutil.FinalityData{
88-
SafeMsgCount: safeMsgCount,
89-
FinalizedMsgCount: finalizedMsgCount,
90-
ValidatedMsgCount: &validatedMsgCount,
91-
}
92-
93-
_, err = c.execClient.SetFinalityData(ctx, finalityData).Await(ctx)
120+
_, err = c.execClient.SetFinalityData(ctx, safeFinalityData, finalizedFinalityData, validatedFinalityData).Await(ctx)
94121
if err != nil {
95122
log.Error("Error pushing finality data from consensus to execution", "err", err)
96123
} else {

arbnode/inbox_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ func (w *execClientWrapper) FullSyncProgressMap(ctx context.Context) map[string]
7171
w.t.Error("not supported")
7272
return nil
7373
}
74-
func (w *execClientWrapper) SetFinalityData(ctx context.Context, finalityData *arbutil.FinalityData) containers.PromiseInterface[struct{}] {
74+
func (w *execClientWrapper) SetFinalityData(
75+
ctx context.Context,
76+
safeFinalityData *arbutil.FinalityData,
77+
finalizedFinalityData *arbutil.FinalityData,
78+
validatedFinalityData *arbutil.FinalityData,
79+
) containers.PromiseInterface[struct{}] {
7580
return containers.NewReadyPromise(struct{}{}, nil)
7681
}
7782

arbnode/node.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ func createNodeImpl(
10881088
consensusExecutionSyncerConfigFetcher := func() *ConsensusExecutionSyncerConfig {
10891089
return &configFetcher.Get().ConsensusExecutionSyncer
10901090
}
1091-
consensusExecutionSyncer := NewConsensusExecutionSyncer(consensusExecutionSyncerConfigFetcher, inboxReader, executionClient, blockValidator)
1091+
consensusExecutionSyncer := NewConsensusExecutionSyncer(consensusExecutionSyncerConfigFetcher, inboxReader, executionClient, blockValidator, txStreamer)
10921092

10931093
return &Node{
10941094
ArbDB: arbDb,
@@ -1407,6 +1407,9 @@ func (n *Node) Start(ctx context.Context) error {
14071407
}
14081408

14091409
func (n *Node) StopAndWait() {
1410+
if n.ConsensusExecutionSyncer != nil {
1411+
n.ConsensusExecutionSyncer.StopAndWait()
1412+
}
14101413
if n.MaintenanceRunner != nil && n.MaintenanceRunner.Started() {
14111414
n.MaintenanceRunner.StopAndWait()
14121415
}

arbutil/finality_data.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
package arbutil
55

6+
import "github.com/ethereum/go-ethereum/common"
7+
68
type FinalityData struct {
7-
FinalizedMsgCount MessageIndex
8-
SafeMsgCount MessageIndex
9-
ValidatedMsgCount *MessageIndex
9+
MsgIdx MessageIndex
10+
BlockHash common.Hash
1011
}

execution/gethexec/executionengine.go

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,17 @@ func (s *ExecutionEngine) Reorg(msgIdxOfFirstMsgToAdd arbutil.MessageIndex, newM
299299
return nil, nil
300300
}
301301

302+
currentSafeBlock := s.bc.CurrentSafeBlock()
303+
if currentSafeBlock != nil && lastBlockToKeep.Number().Cmp(currentSafeBlock.Number) < 0 {
304+
log.Warn("reorg target block is below safe block", "lastBlockNumToKeep", lastBlockNumToKeep, "currentSafeBlock", currentSafeBlock.Number)
305+
s.bc.SetSafe(nil)
306+
}
307+
currentFinalBlock := s.bc.CurrentFinalBlock()
308+
if currentFinalBlock != nil && lastBlockToKeep.Number().Cmp(currentFinalBlock.Number) < 0 {
309+
log.Warn("reorg target block is below final block", "lastBlockNumToKeep", lastBlockNumToKeep, "currentFinalBlock", currentFinalBlock.Number)
310+
s.bc.SetFinalized(nil)
311+
}
312+
302313
tag := s.bc.StateCache().WasmCacheTag()
303314
// reorg Rust-side VM state
304315
C.stylus_reorg_vm(C.uint64_t(lastBlockNumToKeep), C.uint32_t(tag))
@@ -1090,23 +1101,3 @@ func (s *ExecutionEngine) Maintenance(capLimit uint64) error {
10901101
defer s.createBlocksMutex.Unlock()
10911102
return s.bc.FlushTrieDB(common.StorageSize(capLimit))
10921103
}
1093-
1094-
func (s *ExecutionEngine) SetFinalized(finalizedBlockNumber uint64) error {
1095-
block := s.bc.GetBlockByNumber(finalizedBlockNumber)
1096-
if block == nil {
1097-
return errors.New("unable to get block by number")
1098-
}
1099-
1100-
s.bc.SetFinalized(block.Header())
1101-
return nil
1102-
}
1103-
1104-
func (s *ExecutionEngine) SetSafe(safeBlockNumber uint64) error {
1105-
block := s.bc.GetBlockByNumber(safeBlockNumber)
1106-
if block == nil {
1107-
return errors.New("unable to get block by number")
1108-
}
1109-
1110-
s.bc.SetSafe(block.Header())
1111-
return nil
1112-
}

execution/gethexec/node.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,13 @@ func (n *ExecutionNode) FullSyncProgressMap(ctx context.Context) map[string]inte
509509
return n.SyncMonitor.FullSyncProgressMap(ctx)
510510
}
511511

512-
func (n *ExecutionNode) SetFinalityData(ctx context.Context, finalityData *arbutil.FinalityData) containers.PromiseInterface[struct{}] {
513-
err := n.SyncMonitor.SetFinalityData(ctx, finalityData)
512+
func (n *ExecutionNode) SetFinalityData(
513+
ctx context.Context,
514+
safeFinalityData *arbutil.FinalityData,
515+
finalizedFinalityData *arbutil.FinalityData,
516+
validatedFinalityData *arbutil.FinalityData,
517+
) containers.PromiseInterface[struct{}] {
518+
err := n.SyncMonitor.SetFinalityData(ctx, safeFinalityData, finalizedFinalityData, validatedFinalityData)
514519
return containers.NewReadyPromise(struct{}{}, err)
515520
}
516521

execution/gethexec/sync_monitor.go

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gethexec
33
import (
44
"context"
55
"errors"
6+
"fmt"
67

78
flag "github.com/spf13/pflag"
89

@@ -123,47 +124,73 @@ func (s *SyncMonitor) BlockMetadataByNumber(ctx context.Context, blockNum uint64
123124
return nil, nil
124125
}
125126

126-
func (s *SyncMonitor) getFinalityBlock(
127+
func (s *SyncMonitor) getFinalityBlockHeader(
127128
waitForBlockValidator bool,
128-
validatedMsgCount *arbutil.MessageIndex,
129-
finalityMsgCount arbutil.MessageIndex,
130-
) (*types.Block, error) {
129+
validatedFinalityData *arbutil.FinalityData,
130+
finalityFinalityData *arbutil.FinalityData,
131+
) (*types.Header, error) {
132+
if finalityFinalityData == nil {
133+
return nil, nil
134+
}
135+
136+
finalityMsgIdx := finalityFinalityData.MsgIdx
137+
finalityBlockHash := finalityFinalityData.BlockHash
131138
if waitForBlockValidator {
132-
if validatedMsgCount == nil {
139+
if validatedFinalityData == nil {
133140
return nil, errors.New("block validator not set")
134141
}
135-
if finalityMsgCount > *validatedMsgCount {
136-
finalityMsgCount = *validatedMsgCount
142+
if finalityFinalityData.MsgIdx > validatedFinalityData.MsgIdx {
143+
finalityMsgIdx = validatedFinalityData.MsgIdx
144+
finalityBlockHash = validatedFinalityData.BlockHash
137145
}
138146
}
139-
finalityBlockNumber := s.exec.MessageIndexToBlockNumber(finalityMsgCount - 1)
147+
148+
finalityBlockNumber := s.exec.MessageIndexToBlockNumber(finalityMsgIdx)
140149
finalityBlock := s.exec.bc.GetBlockByNumber(finalityBlockNumber)
141150
if finalityBlock == nil {
142-
return nil, errors.New("unable to get block by number")
151+
log.Debug("Finality block not found", "blockNumber", finalityBlockNumber)
152+
return nil, nil
153+
}
154+
if finalityBlock.Hash() != finalityBlockHash {
155+
errorMsg := fmt.Sprintf(
156+
"finality block hash mismatch, blockNumber=%v, block hash provided by consensus=%v, block hash from execution=%v",
157+
finalityBlockNumber,
158+
finalityBlockHash,
159+
finalityBlock.Hash(),
160+
)
161+
return nil, errors.New(errorMsg)
143162
}
144-
return finalityBlock, nil
163+
return finalityBlock.Header(), nil
145164
}
146165

147-
func (s *SyncMonitor) SetFinalityData(ctx context.Context, finalityData *arbutil.FinalityData) error {
148-
finalizedBlock, err := s.getFinalityBlock(
166+
func (s *SyncMonitor) SetFinalityData(
167+
ctx context.Context,
168+
safeFinalityData *arbutil.FinalityData,
169+
finalizedFinalityData *arbutil.FinalityData,
170+
validatedFinalityData *arbutil.FinalityData,
171+
) error {
172+
s.exec.createBlocksMutex.Lock()
173+
defer s.exec.createBlocksMutex.Unlock()
174+
175+
finalizedBlockHeader, err := s.getFinalityBlockHeader(
149176
s.config.FinalizedBlockWaitForBlockValidator,
150-
finalityData.ValidatedMsgCount,
151-
finalityData.FinalizedMsgCount,
177+
validatedFinalityData,
178+
finalizedFinalityData,
152179
)
153180
if err != nil {
154181
return err
155182
}
156-
s.exec.bc.SetFinalized(finalizedBlock.Header())
183+
s.exec.bc.SetFinalized(finalizedBlockHeader)
157184

158-
safeBlock, err := s.getFinalityBlock(
185+
safeBlockHeader, err := s.getFinalityBlockHeader(
159186
s.config.SafeBlockWaitForBlockValidator,
160-
finalityData.ValidatedMsgCount,
161-
finalityData.SafeMsgCount,
187+
validatedFinalityData,
188+
safeFinalityData,
162189
)
163190
if err != nil {
164191
return err
165192
}
166-
s.exec.bc.SetSafe(safeBlock.Header())
193+
s.exec.bc.SetSafe(safeBlockHeader)
167194

168195
return nil
169196
}

execution/interface.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type ExecutionClient interface {
4040
ResultAtMessageIndex(msgIdx arbutil.MessageIndex) containers.PromiseInterface[*MessageResult]
4141
MessageIndexToBlockNumber(messageNum arbutil.MessageIndex) containers.PromiseInterface[uint64]
4242
BlockNumberToMessageIndex(blockNum uint64) containers.PromiseInterface[arbutil.MessageIndex]
43-
SetFinalityData(ctx context.Context, finalityData *arbutil.FinalityData) containers.PromiseInterface[struct{}]
43+
SetFinalityData(ctx context.Context, safeFinalityData *arbutil.FinalityData, finalizedFinalityData *arbutil.FinalityData, validatedFinalityData *arbutil.FinalityData) containers.PromiseInterface[struct{}]
4444
MarkFeedStart(to arbutil.MessageIndex) containers.PromiseInterface[struct{}]
4545

4646
Maintenance() containers.PromiseInterface[struct{}]

0 commit comments

Comments
 (0)