-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add buffered caching of L1 block refs to the confDepth-aware fetcher (#…
…11142) * Add buffered caching of L1 block refs to the confDepth-aware fetcher * Refactor l1 head buffer into helper structs * Fix linting errors * Move L1 block caching from confDepth into an event-driven L1Tracker * Fix l1HeadBuffer locking * Better handle non-shallow reorgs * Improve test naming * Explicitly rewind cache when old head received
- Loading branch information
1 parent
979b5f8
commit 0db615d
Showing
7 changed files
with
479 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package status | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/ethereum-optimism/optimism/op-service/eth" | ||
) | ||
|
||
// l1HeadBuffer is a thread-safe cache for L1 block references, which contains a series blocks with a valid chain of parent hashes. | ||
type l1HeadBuffer struct { | ||
rb *ringbuffer[eth.L1BlockRef] | ||
minBlockNumber uint64 | ||
mu sync.RWMutex | ||
} | ||
|
||
func newL1HeadBuffer(size int) *l1HeadBuffer { | ||
return &l1HeadBuffer{rb: newRingBuffer[eth.L1BlockRef](size)} | ||
} | ||
|
||
// Get returns the L1 block reference for the given block number, if it exists in the cache. | ||
func (lhb *l1HeadBuffer) Get(num uint64) (eth.L1BlockRef, bool) { | ||
lhb.mu.RLock() | ||
defer lhb.mu.RUnlock() | ||
|
||
return lhb.get(num) | ||
} | ||
|
||
func (lhb *l1HeadBuffer) get(num uint64) (eth.L1BlockRef, bool) { | ||
return lhb.rb.Get(int(num - lhb.minBlockNumber)) | ||
} | ||
|
||
// Insert inserts a new L1 block reference into the cache, and removes any entries that are invalidated by a reorg. | ||
// If the parent hash of the new head doesn't match the hash of the previous head, all entries after the new head are removed | ||
// as the chain cannot be validated. | ||
func (lhb *l1HeadBuffer) Insert(l1Head eth.L1BlockRef) { | ||
lhb.mu.Lock() | ||
defer lhb.mu.Unlock() | ||
|
||
if ref, ok := lhb.get(l1Head.Number - 1); ok && ref.Hash == l1Head.ParentHash { | ||
// Parent hash is found, so we can safely add the new head to the cache after the parent. | ||
// Remove any L1 refs from the cache after or conflicting with the new head. | ||
if ref, ok := lhb.rb.End(); ok && ref.Number >= l1Head.Number { | ||
for ref, ok = lhb.rb.Pop(); ok && ref.Number > l1Head.Number; ref, ok = lhb.rb.Pop() { | ||
} | ||
} | ||
} else { | ||
// Parent not found or doesn't match, so invalidate the entire cache. | ||
lhb.rb.Reset() | ||
} | ||
|
||
lhb.rb.Push(l1Head) | ||
|
||
start, _ := lhb.rb.Start() | ||
lhb.minBlockNumber = start.Number | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package status | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" | ||
"github.com/ethereum-optimism/optimism/op-node/rollup/event" | ||
"github.com/ethereum-optimism/optimism/op-service/eth" | ||
) | ||
|
||
// L1Tracker implements the L1Fetcher interface while proactively maintaining a reorg-aware cache | ||
// of L1 block references by number. This handles the L1UnsafeEvent in order to populate the cache with | ||
// the latest L1 block references. | ||
type L1Tracker struct { | ||
derive.L1Fetcher | ||
cache *l1HeadBuffer | ||
} | ||
|
||
func NewL1Tracker(inner derive.L1Fetcher) *L1Tracker { | ||
return &L1Tracker{ | ||
L1Fetcher: inner, | ||
cache: newL1HeadBuffer(1000), | ||
} | ||
} | ||
|
||
func (st *L1Tracker) OnEvent(ev event.Event) bool { | ||
switch x := ev.(type) { | ||
case L1UnsafeEvent: | ||
st.cache.Insert(x.L1Unsafe) | ||
default: | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (l *L1Tracker) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) { | ||
if ref, ok := l.cache.Get(num); ok { | ||
return ref, nil | ||
} | ||
|
||
return l.L1Fetcher.L1BlockRefByNumber(ctx, num) | ||
} |
Oops, something went wrong.