erigon-pulse/cmd/rpcdaemon/commands/otterscan_search_backward.go
Willian Mitsuda 11f4978ed4
Upstream all Otterscan patches to devel (#5945)
Following our previous discussion on erigon's discord, this PR requests
to upstream all Otterscan modifications to erigon's repo.

That decision comes after getting feedback from lots of users at events
this year, and although it may introduce some friction for development,
it will make integrators life easier by having all our modifications
available out of box, e.g., dappnode users will get our RPCs since their
official packages are built from erigon repo.

I'm submitting the source-code as-is, please let me know if you think
there is a better code organization.

The current set of modifications comprises only new RPCs. There are some
proposals for extra-stages that would add new tables, but they are still
WIP and will be submitted separately in future after more testing.
2022-11-03 11:32:15 +07:00

129 lines
2.9 KiB
Go

package commands
import (
"bytes"
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
)
// Given a ChunkLocator, moves back over the chunks and inside each chunk, moves
// backwards over the block numbers.
func NewBackwardBlockProvider(chunkLocator ChunkLocator, maxBlock uint64) BlockProvider {
// block == 0 means no max
if maxBlock == 0 {
maxBlock = MaxBlockNum
}
var iter roaring64.IntIterable64
var chunkProvider ChunkProvider
isFirst := true
finished := false
return func() (uint64, bool, error) {
if finished {
return 0, false, nil
}
if isFirst {
isFirst = false
// Try to get first chunk
var ok bool
var err error
chunkProvider, ok, err = chunkLocator(maxBlock)
if err != nil {
finished = true
return 0, false, err
}
if !ok {
finished = true
return 0, false, nil
}
if chunkProvider == nil {
finished = true
return 0, false, nil
}
// Has at least the first chunk; initialize the iterator
chunk, ok, err := chunkProvider()
if err != nil {
finished = true
return 0, false, err
}
if !ok {
finished = true
return 0, false, nil
}
bm := roaring64.NewBitmap()
if _, err := bm.ReadFrom(bytes.NewReader(chunk)); err != nil {
finished = true
return 0, false, err
}
// It can happen that on the first chunk we'll get a chunk that contains
// the last block <= maxBlock in the middle of the chunk/bitmap, so we
// remove all blocks after it (since there is no AdvanceIfNeeded() in
// IntIterable64)
if maxBlock != MaxBlockNum {
bm.RemoveRange(maxBlock+1, MaxBlockNum)
}
iter = bm.ReverseIterator()
// This means it is the last chunk and the min block is > the last one
if !iter.HasNext() {
chunk, ok, err := chunkProvider()
if err != nil {
finished = true
return 0, false, err
}
if !ok {
finished = true
return 0, false, nil
}
bm := roaring64.NewBitmap()
if _, err := bm.ReadFrom(bytes.NewReader(chunk)); err != nil {
finished = true
return 0, false, err
}
iter = bm.ReverseIterator()
}
}
nextBlock := iter.Next()
hasNext := iter.HasNext()
if !hasNext {
iter = nil
// Check if there is another chunk to get blocks from
chunk, ok, err := chunkProvider()
if err != nil {
return 0, false, err
}
if !ok {
finished = true
return nextBlock, false, nil
}
hasNext = true
bm := roaring64.NewBitmap()
if _, err := bm.ReadFrom(bytes.NewReader(chunk)); err != nil {
finished = true
return 0, false, err
}
iter = bm.ReverseIterator()
}
return nextBlock, hasNext, nil
}
}
func NewCallCursorBackwardBlockProvider(cursor kv.Cursor, addr common.Address, maxBlock uint64) BlockProvider {
chunkLocator := newCallChunkLocator(cursor, addr, false)
return NewBackwardBlockProvider(chunkLocator, maxBlock)
}