github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/blockchain/height.go (about)

     1  package blockchain
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/blocto/solana-go-sdk/client"
     9  	"github.com/blocto/solana-go-sdk/rpc"
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/machinefi/w3bstream/pkg/depends/conf/logger"
    13  	"github.com/machinefi/w3bstream/pkg/depends/kit/logr"
    14  	"github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/builder"
    15  	"github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/datatypes"
    16  	"github.com/machinefi/w3bstream/pkg/models"
    17  	"github.com/machinefi/w3bstream/pkg/types"
    18  )
    19  
    20  type height struct {
    21  	*monitor
    22  	interval time.Duration
    23  }
    24  
    25  func (h *height) run(ctx context.Context) {
    26  	ticker := time.NewTicker(h.interval)
    27  	defer ticker.Stop()
    28  
    29  	for range ticker.C {
    30  		h.do(ctx)
    31  	}
    32  }
    33  
    34  func (h *height) do(ctx context.Context) {
    35  	ctx, l := logger.NewSpanContext(ctx, "bc.height.do")
    36  	defer l.End()
    37  
    38  	d := types.MustMonitorDBExecutorFromContext(ctx)
    39  	chainConf := types.MustChainConfigFromContext(ctx)
    40  	m := &models.ChainHeight{}
    41  
    42  	cs, err := m.List(d, builder.And(m.ColFinished().Eq(datatypes.FALSE), m.ColPaused().Eq(datatypes.FALSE)))
    43  	if err != nil {
    44  		l.Error(errors.Wrap(err, "list chain height db failed"))
    45  		return
    46  	}
    47  	for _, c := range cs {
    48  		chain, ok := chainConf.GetChain(c.ChainID, c.ChainName)
    49  		if !ok {
    50  			l.WithValues("chainID", c.ChainID, "chainName", c.ChainName).Error(errors.New("blockchain not exist"))
    51  			continue
    52  		}
    53  		res, err := h.checkHeightAndSendEvent(ctx, &c, chain)
    54  		if err != nil {
    55  			l.Error(errors.Wrap(err, "check chain height and send event failed"))
    56  			continue
    57  		}
    58  		if res {
    59  			c.Finished = datatypes.TRUE
    60  			c.Uniq = c.ChainHeightID
    61  			if err := c.UpdateByID(d); err != nil {
    62  				l.Error(errors.Wrap(err, "update chain height db failed"))
    63  			}
    64  		}
    65  	}
    66  }
    67  
    68  func (h *height) checkHeightAndSendEvent(ctx context.Context, c *models.ChainHeight, chain *types.Chain) (bool, error) {
    69  	ctx, l := logr.Start(ctx, "bc.height.checkHeightAndSendEvent")
    70  	defer l.End()
    71  
    72  	l = l.WithValues("type", "chain_height", "chain_height_id", c.ChainHeightID)
    73  
    74  	headerNumber, err := h.getHeaderNumber(ctx, chain)
    75  	if err != nil {
    76  		l.Error(err)
    77  		return false, err
    78  	}
    79  	if headerNumber < c.Height {
    80  		l.WithValues("headerNumber", headerNumber, "chainHeight", c.Height).Debug("did not arrive")
    81  		return false, nil
    82  	}
    83  	data, err := json.Marshal(struct {
    84  		HeaderNumber uint64
    85  	}{
    86  		headerNumber,
    87  	})
    88  	if err != nil {
    89  		l.Error(err)
    90  		return false, err
    91  	}
    92  	if err := h.sendEvent(ctx, data, c.ProjectName, c.EventType); err != nil {
    93  		l.Error(err)
    94  		return false, err
    95  	}
    96  	return true, nil
    97  }
    98  
    99  func (h *height) getHeaderNumber(ctx context.Context, chain *types.Chain) (uint64, error) {
   100  	switch {
   101  	case chain.IsEth():
   102  		cli, ok := types.MustETHClientConfigFromContext(ctx).Clients[uint32(chain.ChainID)]
   103  		if !ok {
   104  			return 0, errors.Errorf("chain id not exists: %d", chain.ChainID)
   105  		}
   106  
   107  		header, err := cli.HeaderByNumber(context.Background(), nil)
   108  		if err != nil {
   109  			return 0, err
   110  		}
   111  		return header.Number.Uint64(), nil
   112  
   113  	case chain.IsSolana():
   114  		cli := client.NewClient(chain.Endpoint)
   115  		res, err := cli.RpcClient.GetBlockHeightWithConfig(context.Background(), rpc.GetBlockHeightConfig{Commitment: rpc.CommitmentFinalized})
   116  		if err != nil {
   117  			return 0, err
   118  		}
   119  		if res.Error != nil {
   120  			return 0, res.Error
   121  		}
   122  		return res.Result, nil
   123  
   124  	default:
   125  		return 0, errors.New("unsupported chain")
   126  	}
   127  }