diff --git a/internal/era/iterator.go b/internal/era/iterator.go index b798af90db..291d55a27d 100644 --- a/internal/era/iterator.go +++ b/internal/era/iterator.go @@ -17,6 +17,7 @@ package era import ( + "fmt" "io" "math/big" @@ -29,7 +30,8 @@ type Iterator struct { inner *RawIterator } -// NewIterator returns a new Iterator instance. +// NewRawIterator returns a new Iterator instance. Next must be immediately +// called on new iterators to load the first item. func NewIterator(e *Era) (*Iterator, error) { inner, err := NewRawIterator(e) if err != nil { @@ -38,7 +40,9 @@ func NewIterator(e *Era) (*Iterator, error) { return &Iterator{inner}, nil } -// Next moves the iterator to the next block entry. +// Next moves the iterator to the next block entry. It returns false when all +// items have been read or an error has halted its progress. Block, Receipts, +// and BlockAndReceipts should no longer be called after false is returned. func (it *Iterator) Next() bool { return it.inner.Next() } @@ -48,13 +52,17 @@ func (it *Iterator) Number() uint64 { return it.inner.next - 1 } -// Error returns the error status of the iterator. +// Error returns the error status of the iterator. It should be called before +// reading from any of the iterator's values. func (it *Iterator) Error() error { return it.inner.Error() } // Block returns the block for the iterator's current position. func (it *Iterator) Block() (*types.Block, error) { + if it.inner.Header == nil || it.inner.Body == nil { + return nil, fmt.Errorf("header and body must be non-nil") + } var ( header types.Header body types.Body @@ -70,6 +78,9 @@ func (it *Iterator) Block() (*types.Block, error) { // Receipts returns the receipts for the iterator's current position. func (it *Iterator) Receipts() (types.Receipts, error) { + if it.inner.Receipts == nil { + return nil, fmt.Errorf("receipts must be non-nil") + } var receipts types.Receipts err := rlp.Decode(it.inner.Receipts, &receipts) return receipts, err @@ -111,7 +122,8 @@ type RawIterator struct { TotalDifficulty io.Reader } -// NewRawIterator returns a new Iterator instance. +// NewRawIterator returns a new RawIterator instance. Next must be immediately +// called on new iterators to load the first item. func NewRawIterator(e *Era) (*RawIterator, error) { return &RawIterator{ e: e, @@ -119,30 +131,43 @@ func NewRawIterator(e *Era) (*RawIterator, error) { }, nil } -// Next moves the iterator to the next block entry. +// Next moves the iterator to the next block entry. It returns false when all +// items have been read or an error has halted its progress. Header, Body, +// Receipts, TotalDifficulty will be set to nil in the case returning false or +// finding an error and should therefore no longer be read from. func (it *RawIterator) Next() bool { + // Clear old errors. + it.err = nil if it.e.m.start+it.e.m.count <= it.next { + it.clear() return false } off, err := it.e.readOffset(it.next) - if err == io.EOF { + if err != nil { + // Error here means block index is corrupted, so don't + // continue. + it.clear() it.err = err return false } var n int64 if it.Header, n, it.err = newSnappyReader(it.e.s, TypeCompressedHeader, off); it.err != nil { + it.clear() return true } off += n if it.Body, n, it.err = newSnappyReader(it.e.s, TypeCompressedBody, off); it.err != nil { + it.clear() return true } off += n if it.Receipts, n, it.err = newSnappyReader(it.e.s, TypeCompressedReceipts, off); it.err != nil { + it.clear() return true } off += n if it.TotalDifficulty, _, it.err = it.e.s.ReaderAt(TypeTotalDifficulty, off); it.err != nil { + it.clear() return true } it.next += 1 @@ -154,10 +179,19 @@ func (it *RawIterator) Number() uint64 { return it.next - 1 } -// Error returns the error status of the iterator. +// Error returns the error status of the iterator. It should be called before +// reading from any of the iterator's values. func (it *RawIterator) Error() error { if it.err == io.EOF { return nil } return it.err } + +// clear sets all the outputs to nil. +func (it *RawIterator) clear() { + it.Header = nil + it.Body = nil + it.Receipts = nil + it.TotalDifficulty = nil +}