66 "flare-ftso-indexer/chain"
77 "flare-ftso-indexer/config"
88 "flare-ftso-indexer/logger"
9+ "math"
910 "math/big"
10- "sort"
1111 "time"
1212
1313 "github.com/pkg/errors"
@@ -17,6 +17,7 @@ import (
1717const (
1818 deleteBatchesPauseAfter = 10
1919 deleteBatchesPauseDuration = 100 * time .Millisecond
20+ searchWindowBlocks = uint64 (5 * 24 * 3600 ) // Heuristic: 5 days of blocks, assuming 1 sec per block
2021)
2122
2223func DropHistory (
@@ -226,42 +227,66 @@ func getNearestBlockByTimestampFromChain(
226227 startBlockNumber uint64 ,
227228 endBlockNumber uint64 ,
228229) (uint64 , error ) {
229- // We search over the entire range of blocks from the configured
230- // startBlockNumber to the most recent block number.
231- //
232- // This could potentially be optimized further using some estimate
233- // of the block time to reduce the search range, but for now this should
234- // be good enough.
235- //
236- // Once the indexer is running for a while it should be possible to
237- // read the required block number from the database instead of using
238- // this search process.
239-
240- var err error
241- i := sort .Search (int (endBlockNumber - startBlockNumber + 1 ), func (i int ) bool {
242- // The err variable comes from the enclosing function. If it has been
243- // set to a non-nil value by a previous iteration of the binary search,
244- // we should not overwrite it. Ideally we would exit the binary search
245- // early, but the sort.Search function does not provide a way to do
246- // that. So instead, we just return false for all future iterations.
247- // The results of the search are meaningless in this case.
248- if err != nil {
249- return false
230+ logger .Debug ("Getting nearest block by timestamp from chain: search timestamp: %d, start block: %d, end block: %d" , searchTimestamp , startBlockNumber , endBlockNumber )
231+
232+ var searchStartBlockNumber = startBlockNumber
233+ var searchEndBlockNumber = endBlockNumber
234+
235+ if startBlockNumber == 0 {
236+ // If start block was not specified in config, try to reduce the search space by going back in steps of 5 days
237+ // until we find a block earlier than searchTimestamp.
238+ // This is to avoid querying for very old blocks during binary search - the RPC node might not have full block history.
239+ startCandidate := endBlockNumber
240+ candidateBlockTime := uint64 (math .MaxUint64 )
241+
242+ var err error
243+ for candidateBlockTime > searchTimestamp {
244+ startCandidate = startCandidate - searchWindowBlocks
245+ candidateBlockTime , _ , err = getBlockTimestamp (ctx , big .NewInt (int64 (startCandidate )), client )
246+ if err != nil {
247+ return 0 , errors .Wrap (err , "getNearestBlockByTimestampFromChain" )
248+ }
250249 }
250+ searchStartBlockNumber = startCandidate
251+ searchEndBlockNumber = startCandidate + searchWindowBlocks
252+ logger .Debug ("Search block window narrowed down to: %d-%d" , searchStartBlockNumber , searchEndBlockNumber )
253+ }
251254
252- blockNumber := startBlockNumber + uint64 (i )
255+ blockNumber , err := binarySearchBlockByTimestamp (
256+ ctx , searchTimestamp , client , searchStartBlockNumber , searchEndBlockNumber ,
257+ )
258+ if err != nil {
259+ return 0 , errors .Wrap (err , "getNearestBlockByTimestampFromChain" )
260+ }
261+
262+ logger .Debug ("Found nearest block by timestamp from chain: block number: %d" , blockNumber )
263+ return blockNumber , nil
264+ }
253265
254- var blockTime uint64
255- blockTime , _ , err = getBlockTimestamp (ctx , big .NewInt (int64 (blockNumber )), client )
266+ func binarySearchBlockByTimestamp (
267+ ctx context.Context ,
268+ searchTimestamp uint64 ,
269+ client * chain.Client ,
270+ startBlockNumber uint64 ,
271+ endBlockNumber uint64 ,
272+ ) (uint64 , error ) {
273+ low := startBlockNumber
274+ high := endBlockNumber
275+
276+ for low < high {
277+ mid := low + (high - low )/ 2
278+
279+ blockTime , _ , err := getBlockTimestamp (ctx , big .NewInt (int64 (mid )), client )
256280 if err != nil {
257- return false
281+ return 0 , err
258282 }
259283
260- return blockTime >= searchTimestamp
261- })
262- if err != nil {
263- return 0 , errors .Wrap (err , "getNearestBlockByTimestampFromChain" )
284+ if blockTime >= searchTimestamp {
285+ high = mid
286+ } else {
287+ low = mid + 1
288+ }
264289 }
265290
266- return startBlockNumber + uint64 ( i ) , nil
291+ return low , nil
267292}
0 commit comments