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 }