iterator.go 4.28 KB
Newer Older
1
package logs
2 3

import (
4
	"errors"
5 6
	"fmt"
	"io"
7

8 9
	"github.com/ethereum/go-ethereum/common"

10
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb"
11
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
12 13
)

14 15
type IteratorState interface {
	NextIndex() entrydb.EntryIdx
16
	SealedBlock() (hash common.Hash, num uint64, ok bool)
17
	SealedTimestamp() (timestamp uint64, ok bool)
18
	InitMessage() (hash common.Hash, logIndex uint32, ok bool)
19 20 21
	ExecMessage() *types.ExecutingMessage
}

22
type Iterator interface {
23 24 25 26
	End() error
	NextInitMsg() error
	NextExecMsg() error
	NextBlock() error
27
	TraverseConditional(traverseConditionalFn) error
28
	IteratorState
29 30
}

31
type iterator struct {
32 33
	db          *DB
	current     logContext
34 35 36
	entriesRead int64
}

37 38
type traverseConditionalFn func(state IteratorState) error

39 40 41 42 43
// End traverses the iterator to the end of the DB.
// It does not return io.EOF or ErrFuture.
func (i *iterator) End() error {
	for {
		_, err := i.next()
44
		if errors.Is(err, types.ErrFuture) {
45 46 47 48 49 50 51
			return nil
		} else if err != nil {
			return err
		}
	}
}

52
// NextInitMsg advances the iterator until it reads the next Initiating Message into the current state.
53 54 55 56 57
// It scans forward until it finds and fully reads an initiating event, skipping any blocks.
func (i *iterator) NextInitMsg() error {
	seenLog := false
	for {
		typ, err := i.next()
58
		if err != nil {
59 60
			return err
		}
61
		if typ == TypeInitiatingEvent {
62
			seenLog = true
63
		}
64 65 66 67 68 69 70 71
		if !i.current.hasCompleteBlock() {
			continue // must know the block we're building on top of
		}
		if i.current.hasIncompleteLog() {
			continue // didn't finish processing the log yet
		}
		if seenLog {
			return nil
72 73 74
		}
	}
}
75

76
// NextExecMsg advances the iterator until it reads the next Executing Message into the current state.
77 78 79 80 81 82 83 84 85 86 87
// It scans forward until it finds and fully reads an initiating event, skipping any blocks.
func (i *iterator) NextExecMsg() error {
	for {
		err := i.NextInitMsg()
		if err != nil {
			return err
		}
		if i.current.execMsg != nil {
			return nil // found a new executing message!
		}
	}
88 89
}

90
// NextBlock advances the iterator until it reads the next block into the current state.
91 92 93 94 95 96 97 98
// It scans forward until it finds and fully reads a block, skipping any events.
func (i *iterator) NextBlock() error {
	seenBlock := false
	for {
		typ, err := i.next()
		if err != nil {
			return err
		}
99
		if typ == TypeSearchCheckpoint {
100 101 102 103 104 105 106 107
			seenBlock = true
		}
		if !i.current.hasCompleteBlock() {
			continue // need the full block content
		}
		if seenBlock {
			return nil
		}
108 109 110
	}
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
func (i *iterator) TraverseConditional(fn traverseConditionalFn) error {
	var snapshot logContext
	for {
		snapshot = i.current // copy the iterator state
		_, err := i.next()
		if err != nil {
			i.current = snapshot
			return err
		}
		if i.current.need != 0 { // skip intermediate states
			continue
		}
		if err := fn(&i.current); err != nil {
			i.current = snapshot
			return err
		}
	}
}

130
// Read and apply the next entry.
131
func (i *iterator) next() (EntryType, error) {
132 133 134 135
	index := i.current.nextEntryIndex
	entry, err := i.db.store.Read(index)
	if err != nil {
		if errors.Is(err, io.EOF) {
136
			return 0, types.ErrFuture
137 138
		}
		return 0, fmt.Errorf("failed to read entry %d: %w", index, err)
139
	}
140 141
	if err := i.current.ApplyEntry(entry); err != nil {
		return entry.Type(), fmt.Errorf("failed to apply entry %d to iterator state: %w", index, err)
142 143
	}

144 145 146 147 148 149 150 151 152 153
	i.entriesRead++
	return entry.Type(), nil
}

func (i *iterator) NextIndex() entrydb.EntryIdx {
	return i.current.NextIndex()
}

// SealedBlock returns the sealed block that we are appending logs after, if any is available.
// I.e. the block is the parent block of the block containing the logs that are currently appending to it.
154
func (i *iterator) SealedBlock() (hash common.Hash, num uint64, ok bool) {
155 156 157
	return i.current.SealedBlock()
}

158 159 160 161 162
// SealedTimestamp returns the timestamp of SealedBlock
func (i *iterator) SealedTimestamp() (timestamp uint64, ok bool) {
	return i.current.SealedTimestamp()
}

163
// InitMessage returns the current initiating message, if any is available.
164
func (i *iterator) InitMessage() (hash common.Hash, logIndex uint32, ok bool) {
165 166 167 168 169 170
	return i.current.InitMessage()
}

// ExecMessage returns the current executing message, if any is available.
func (i *iterator) ExecMessage() *types.ExecutingMessage {
	return i.current.ExecMessage()
171
}