github.com/MetalBlockchain/metalgo@v1.11.9/indexer/service.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package indexer
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"time"
    10  
    11  	"github.com/MetalBlockchain/metalgo/database"
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils/formatting"
    14  	"github.com/MetalBlockchain/metalgo/utils/json"
    15  )
    16  
    17  type service struct {
    18  	index *index
    19  }
    20  
    21  type FormattedContainer struct {
    22  	ID        ids.ID              `json:"id"`
    23  	Bytes     string              `json:"bytes"`
    24  	Timestamp time.Time           `json:"timestamp"`
    25  	Encoding  formatting.Encoding `json:"encoding"`
    26  	Index     json.Uint64         `json:"index"`
    27  }
    28  
    29  func newFormattedContainer(c Container, index uint64, enc formatting.Encoding) (FormattedContainer, error) {
    30  	fc := FormattedContainer{
    31  		Encoding: enc,
    32  		ID:       c.ID,
    33  		Index:    json.Uint64(index),
    34  	}
    35  	bytesStr, err := formatting.Encode(enc, c.Bytes)
    36  	if err != nil {
    37  		return fc, err
    38  	}
    39  	fc.Bytes = bytesStr
    40  	fc.Timestamp = time.Unix(0, c.Timestamp)
    41  	return fc, nil
    42  }
    43  
    44  type GetLastAcceptedArgs struct {
    45  	Encoding formatting.Encoding `json:"encoding"`
    46  }
    47  
    48  func (s *service) GetLastAccepted(_ *http.Request, args *GetLastAcceptedArgs, reply *FormattedContainer) error {
    49  	container, err := s.index.GetLastAccepted()
    50  	if err != nil {
    51  		return err
    52  	}
    53  	index, err := s.index.GetIndex(container.ID)
    54  	if err != nil {
    55  		return fmt.Errorf("couldn't get index: %w", err)
    56  	}
    57  	*reply, err = newFormattedContainer(container, index, args.Encoding)
    58  	return err
    59  }
    60  
    61  type GetContainerByIndexArgs struct {
    62  	Index    json.Uint64         `json:"index"`
    63  	Encoding formatting.Encoding `json:"encoding"`
    64  }
    65  
    66  func (s *service) GetContainerByIndex(_ *http.Request, args *GetContainerByIndexArgs, reply *FormattedContainer) error {
    67  	container, err := s.index.GetContainerByIndex(uint64(args.Index))
    68  	if err != nil {
    69  		return err
    70  	}
    71  	index, err := s.index.GetIndex(container.ID)
    72  	if err != nil {
    73  		return fmt.Errorf("couldn't get index: %w", err)
    74  	}
    75  	*reply, err = newFormattedContainer(container, index, args.Encoding)
    76  	return err
    77  }
    78  
    79  type GetContainerRangeArgs struct {
    80  	StartIndex json.Uint64         `json:"startIndex"`
    81  	NumToFetch json.Uint64         `json:"numToFetch"`
    82  	Encoding   formatting.Encoding `json:"encoding"`
    83  }
    84  
    85  type GetContainerRangeResponse struct {
    86  	Containers []FormattedContainer `json:"containers"`
    87  }
    88  
    89  // GetContainerRange returns the transactions at index [startIndex], [startIndex+1], ... , [startIndex+n-1]
    90  // If [n] == 0, returns an empty response (i.e. null).
    91  // If [startIndex] > the last accepted index, returns an error (unless the above apply.)
    92  // If [n] > [MaxFetchedByRange], returns an error.
    93  // If we run out of transactions, returns the ones fetched before running out.
    94  func (s *service) GetContainerRange(_ *http.Request, args *GetContainerRangeArgs, reply *GetContainerRangeResponse) error {
    95  	containers, err := s.index.GetContainerRange(uint64(args.StartIndex), uint64(args.NumToFetch))
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	reply.Containers = make([]FormattedContainer, len(containers))
   101  	for i, container := range containers {
   102  		index, err := s.index.GetIndex(container.ID)
   103  		if err != nil {
   104  			return fmt.Errorf("couldn't get index: %w", err)
   105  		}
   106  		reply.Containers[i], err = newFormattedContainer(container, index, args.Encoding)
   107  		if err != nil {
   108  			return err
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  type GetIndexArgs struct {
   115  	ID ids.ID `json:"id"`
   116  }
   117  
   118  type GetIndexResponse struct {
   119  	Index json.Uint64 `json:"index"`
   120  }
   121  
   122  func (s *service) GetIndex(_ *http.Request, args *GetIndexArgs, reply *GetIndexResponse) error {
   123  	index, err := s.index.GetIndex(args.ID)
   124  	reply.Index = json.Uint64(index)
   125  	return err
   126  }
   127  
   128  type IsAcceptedArgs struct {
   129  	ID ids.ID `json:"id"`
   130  }
   131  
   132  type IsAcceptedResponse struct {
   133  	IsAccepted bool `json:"isAccepted"`
   134  }
   135  
   136  func (s *service) IsAccepted(_ *http.Request, args *IsAcceptedArgs, reply *IsAcceptedResponse) error {
   137  	_, err := s.index.GetIndex(args.ID)
   138  	if err == nil {
   139  		reply.IsAccepted = true
   140  		return nil
   141  	}
   142  	if err == database.ErrNotFound {
   143  		reply.IsAccepted = false
   144  		return nil
   145  	}
   146  	return err
   147  }
   148  
   149  type GetContainerByIDArgs struct {
   150  	ID       ids.ID              `json:"id"`
   151  	Encoding formatting.Encoding `json:"encoding"`
   152  }
   153  
   154  func (s *service) GetContainerByID(_ *http.Request, args *GetContainerByIDArgs, reply *FormattedContainer) error {
   155  	container, err := s.index.GetContainerByID(args.ID)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	index, err := s.index.GetIndex(container.ID)
   160  	if err != nil {
   161  		return fmt.Errorf("couldn't get index: %w", err)
   162  	}
   163  	*reply, err = newFormattedContainer(container, index, args.Encoding)
   164  	return err
   165  }