2023-01-05 12:45:09 +01:00
|
|
|
package syncer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-03-08 15:30:40 +01:00
|
|
|
"time"
|
2023-01-05 12:45:09 +01:00
|
|
|
|
|
|
|
"github.com/alitto/pond"
|
|
|
|
"github.com/celo-org/celo-blockchain/core/types"
|
|
|
|
"github.com/celo-org/celo-blockchain/ethclient"
|
2023-04-14 10:27:08 +02:00
|
|
|
"github.com/celo-org/celo-blockchain/event"
|
2023-01-05 12:45:09 +01:00
|
|
|
"github.com/grassrootseconomics/cic-chain-events/internal/pipeline"
|
|
|
|
"github.com/zerodha/logf"
|
|
|
|
)
|
|
|
|
|
2023-03-08 15:30:40 +01:00
|
|
|
const (
|
2023-04-14 10:27:08 +02:00
|
|
|
jobTimeout = 5 * time.Second
|
|
|
|
resubscribeBackoff = 2 * time.Second
|
2023-03-08 15:30:40 +01:00
|
|
|
)
|
2023-01-05 12:45:09 +01:00
|
|
|
|
2023-03-08 15:30:40 +01:00
|
|
|
type (
|
|
|
|
HeadSyncerOpts struct {
|
|
|
|
Logg logf.Logger
|
|
|
|
Pipeline *pipeline.Pipeline
|
|
|
|
Pool *pond.WorkerPool
|
|
|
|
Stats *Stats
|
|
|
|
WsEndpoint string
|
|
|
|
}
|
|
|
|
|
|
|
|
HeadSyncer struct {
|
|
|
|
ethClient *ethclient.Client
|
|
|
|
logg logf.Logger
|
|
|
|
pipeline *pipeline.Pipeline
|
|
|
|
pool *pond.WorkerPool
|
|
|
|
stats *Stats
|
|
|
|
}
|
|
|
|
)
|
2023-01-05 12:45:09 +01:00
|
|
|
|
|
|
|
func NewHeadSyncer(o HeadSyncerOpts) (*HeadSyncer, error) {
|
|
|
|
ethClient, err := ethclient.Dial(o.WsEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &HeadSyncer{
|
|
|
|
ethClient: ethClient,
|
2023-03-08 15:30:40 +01:00
|
|
|
logg: o.Logg,
|
|
|
|
pipeline: o.Pipeline,
|
2023-01-05 12:45:09 +01:00
|
|
|
pool: o.Pool,
|
2023-03-08 15:30:40 +01:00
|
|
|
stats: o.Stats,
|
2023-01-05 12:45:09 +01:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-01-11 10:29:16 +01:00
|
|
|
// Start creates a websocket subscription and actively receives new blocks until stopped
|
2023-01-11 09:13:59 +01:00
|
|
|
// or a critical error occurs.
|
2023-01-05 12:45:09 +01:00
|
|
|
func (hs *HeadSyncer) Start(ctx context.Context) error {
|
2023-01-11 09:13:59 +01:00
|
|
|
headerReceiver := make(chan *types.Header, 1)
|
2023-01-05 12:45:09 +01:00
|
|
|
|
2023-04-14 10:27:08 +02:00
|
|
|
sub := event.ResubscribeErr(resubscribeBackoff, func(ctx context.Context, err error) (event.Subscription, error) {
|
|
|
|
if err != nil {
|
|
|
|
hs.logg.Error("head syncer: resubscribe error", "error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return hs.ethClient.SubscribeNewHead(ctx, headerReceiver)
|
|
|
|
})
|
2023-01-11 09:13:59 +01:00
|
|
|
defer sub.Unsubscribe()
|
2023-01-05 12:45:09 +01:00
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
2023-01-11 09:13:59 +01:00
|
|
|
case <-ctx.Done():
|
|
|
|
hs.logg.Info("head syncer: shutdown signal received")
|
|
|
|
return nil
|
2023-01-05 12:45:09 +01:00
|
|
|
case header := <-headerReceiver:
|
2023-01-11 09:13:59 +01:00
|
|
|
blockNumber := header.Number.Uint64()
|
|
|
|
hs.logg.Debug("head syncer: received new block", "block", blockNumber)
|
|
|
|
hs.stats.UpdateHeadCursor(blockNumber)
|
2023-01-05 12:45:09 +01:00
|
|
|
hs.pool.Submit(func() {
|
2023-03-08 15:30:40 +01:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), jobTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
if err := hs.pipeline.Run(ctx, blockNumber); err != nil {
|
2023-04-14 10:27:08 +02:00
|
|
|
hs.logg.Error("head syncer: pipeline run error", "error", err)
|
2023-01-05 12:45:09 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|