package transfer import ( "context" "errors" "math/big" "go.uber.org/zap" "github.com/status-im/status-go/logutils" ) // SetupIterativeDownloader configures IterativeDownloader with last known synced block. func SetupIterativeDownloader( client HeaderReader, downloader BatchDownloader, size *big.Int, to *big.Int, from *big.Int) (*IterativeDownloader, error) { if to == nil || from == nil { return nil, errors.New("to or from cannot be nil") } logutils.ZapLogger().Debug("iterative downloader", zap.Stringer("from", from), zap.Stringer("to", to), zap.Stringer("size", size), ) d := &IterativeDownloader{ client: client, batchSize: size, downloader: downloader, from: from, to: to, } return d, nil } // BatchDownloader interface for loading transfers in batches in speificed range of blocks. type BatchDownloader interface { GetHeadersInRange(ctx context.Context, from, to *big.Int) ([]*DBHeader, error) } // IterativeDownloader downloads batches of transfers in a specified size. type IterativeDownloader struct { client HeaderReader batchSize *big.Int downloader BatchDownloader from, to *big.Int previous *big.Int } // Finished true when earliest block with given sync option is zero. func (d *IterativeDownloader) Finished() bool { return d.from.Cmp(d.to) == 0 } // Header return last synced header. func (d *IterativeDownloader) Header() *big.Int { return d.previous } // Next moves closer to the end on every new iteration. func (d *IterativeDownloader) Next(parent context.Context) ([]*DBHeader, *big.Int, *big.Int, error) { to := d.to from := new(big.Int).Sub(to, d.batchSize) // if start < 0; start = 0 if from.Cmp(d.from) == -1 { from = d.from } headers, err := d.downloader.GetHeadersInRange(parent, from, to) logutils.ZapLogger().Debug("load erc20 transfers in range", zap.Stringer("from", from), zap.Stringer("to", to), zap.Stringer("batchSize", d.batchSize), ) if err != nil { logutils.ZapLogger().Error("failed to get transfer in between two blocks", zap.Stringer("from", from), zap.Stringer("to", to), zap.Error(err), ) return nil, nil, nil, err } d.previous, d.to = d.to, from return headers, d.from, to, nil } // Revert reverts last step progress. Should be used if application failed to process transfers. // For example failed to persist them. func (d *IterativeDownloader) Revert() { if d.previous != nil { d.from = d.previous } }