github.com/celestiaorg/celestia-node@v0.15.0-beta.1/nodebuilder/header/service.go (about)

     1  package header
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	libhead "github.com/celestiaorg/go-header"
     8  	"github.com/celestiaorg/go-header/p2p"
     9  	"github.com/celestiaorg/go-header/sync"
    10  
    11  	"github.com/celestiaorg/celestia-node/header"
    12  )
    13  
    14  // Service represents the header Service that can be started / stopped on a node.
    15  // Service's main function is to manage its sub-services. Service can contain several
    16  // sub-services, such as Exchange, ExchangeServer, Syncer, and so forth.
    17  type Service struct {
    18  	ex libhead.Exchange[*header.ExtendedHeader]
    19  
    20  	syncer    syncer
    21  	sub       libhead.Subscriber[*header.ExtendedHeader]
    22  	p2pServer *p2p.ExchangeServer[*header.ExtendedHeader]
    23  	store     libhead.Store[*header.ExtendedHeader]
    24  }
    25  
    26  // syncer bare minimum Syncer interface for testing
    27  type syncer interface {
    28  	libhead.Head[*header.ExtendedHeader]
    29  
    30  	State() sync.State
    31  	SyncWait(ctx context.Context) error
    32  }
    33  
    34  // newHeaderService creates a new instance of header Service.
    35  func newHeaderService(
    36  	syncer *sync.Syncer[*header.ExtendedHeader],
    37  	sub libhead.Subscriber[*header.ExtendedHeader],
    38  	p2pServer *p2p.ExchangeServer[*header.ExtendedHeader],
    39  	ex libhead.Exchange[*header.ExtendedHeader],
    40  	store libhead.Store[*header.ExtendedHeader],
    41  ) Module {
    42  	return &Service{
    43  		syncer:    syncer,
    44  		sub:       sub,
    45  		p2pServer: p2pServer,
    46  		ex:        ex,
    47  		store:     store,
    48  	}
    49  }
    50  
    51  func (s *Service) GetByHash(ctx context.Context, hash libhead.Hash) (*header.ExtendedHeader, error) {
    52  	return s.store.Get(ctx, hash)
    53  }
    54  
    55  func (s *Service) GetRangeByHeight(
    56  	ctx context.Context,
    57  	from *header.ExtendedHeader,
    58  	to uint64,
    59  ) ([]*header.ExtendedHeader, error) {
    60  	return s.store.GetRangeByHeight(ctx, from, to)
    61  }
    62  
    63  func (s *Service) GetByHeight(ctx context.Context, height uint64) (*header.ExtendedHeader, error) {
    64  	head, err := s.syncer.Head(ctx)
    65  	switch {
    66  	case err != nil:
    67  		return nil, err
    68  	case head.Height() == height:
    69  		return head, nil
    70  	case head.Height()+1 < height:
    71  		return nil, fmt.Errorf("header: given height is from the future: "+
    72  			"networkHeight: %d, requestedHeight: %d", head.Height(), height)
    73  	}
    74  
    75  	// TODO(vgonkivs): remove after https://github.com/celestiaorg/go-header/issues/32 is
    76  	//  implemented and fetch header from HeaderEx if missing locally
    77  	head, err = s.store.Head(ctx)
    78  	switch {
    79  	case err != nil:
    80  		return nil, err
    81  	case head.Height() == height:
    82  		return head, nil
    83  	// `+1` allows for one header network lag, e.g. user request header that is milliseconds away
    84  	case head.Height()+1 < height:
    85  		return nil, fmt.Errorf("header: syncing in progress: "+
    86  			"localHeadHeight: %d, requestedHeight: %d", head.Height(), height)
    87  	default:
    88  		return s.store.GetByHeight(ctx, height)
    89  	}
    90  }
    91  
    92  func (s *Service) WaitForHeight(ctx context.Context, height uint64) (*header.ExtendedHeader, error) {
    93  	return s.store.GetByHeight(ctx, height)
    94  }
    95  
    96  func (s *Service) LocalHead(ctx context.Context) (*header.ExtendedHeader, error) {
    97  	return s.store.Head(ctx)
    98  }
    99  
   100  func (s *Service) SyncState(context.Context) (sync.State, error) {
   101  	return s.syncer.State(), nil
   102  }
   103  
   104  func (s *Service) SyncWait(ctx context.Context) error {
   105  	return s.syncer.SyncWait(ctx)
   106  }
   107  
   108  func (s *Service) NetworkHead(ctx context.Context) (*header.ExtendedHeader, error) {
   109  	return s.syncer.Head(ctx)
   110  }
   111  
   112  func (s *Service) Subscribe(ctx context.Context) (<-chan *header.ExtendedHeader, error) {
   113  	subscription, err := s.sub.Subscribe()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	headerCh := make(chan *header.ExtendedHeader)
   119  	go func() {
   120  		defer close(headerCh)
   121  		defer subscription.Cancel()
   122  
   123  		for {
   124  			h, err := subscription.NextHeader(ctx)
   125  			if err != nil {
   126  				if err != context.DeadlineExceeded && err != context.Canceled {
   127  					log.Errorw("fetching header from subscription", "err", err)
   128  				}
   129  				return
   130  			}
   131  
   132  			select {
   133  			case <-ctx.Done():
   134  				return
   135  			case headerCh <- h:
   136  			}
   137  		}
   138  	}()
   139  	return headerCh, nil
   140  }