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 }