code.vegaprotocol.io/vega@v0.79.0/core/api/grpc.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package api 17 18 import ( 19 "context" 20 "net" 21 "strconv" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/stats" 26 "code.vegaprotocol.io/vega/core/vegatime" 27 vgcontext "code.vegaprotocol.io/vega/libs/context" 28 "code.vegaprotocol.io/vega/libs/subscribers" 29 "code.vegaprotocol.io/vega/logging" 30 protoapi "code.vegaprotocol.io/vega/protos/vega/api/v1" 31 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 32 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 33 34 tmctypes "github.com/cometbft/cometbft/rpc/core/types" 35 "google.golang.org/grpc" 36 "google.golang.org/grpc/metadata" 37 "google.golang.org/grpc/peer" 38 ) 39 40 //go:generate go run github.com/golang/mock/mockgen -destination mocks/event_service_mock.go -package mocks code.vegaprotocol.io/vega/core/api EventService 41 type EventService interface { 42 ObserveEvents(ctx context.Context, retries int, eTypes []events.Type, batchSize int, filters ...subscribers.EventFilter) (<-chan []*eventspb.BusEvent, chan<- int) 43 } 44 45 // TimeService ... 46 // 47 //go:generate go run github.com/golang/mock/mockgen -destination mocks/time_service_mock.go -package mocks code.vegaprotocol.io/vega/core/api TimeService 48 type TimeService interface { 49 GetTimeNow() time.Time 50 } 51 52 // EvtForwarder 53 // 54 //go:generate go run github.com/golang/mock/mockgen -destination mocks/evt_forwarder_mock.go -package mocks code.vegaprotocol.io/vega/core/api EvtForwarder 55 type EvtForwarder interface { 56 Forward(ctx context.Context, e *commandspb.ChainEvent, pk string) error 57 } 58 59 // Blockchain ... 60 // 61 //nolint:interfacebloat 62 //go:generate go run github.com/golang/mock/mockgen -destination mocks/blockchain_mock.go -package mocks code.vegaprotocol.io/vega/core/api Blockchain 63 type Blockchain interface { 64 SubmitTransactionSync(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTx, error) 65 SubmitTransactionAsync(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTx, error) 66 SubmitTransactionCommit(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultBroadcastTxCommit, error) 67 SubmitRawTransactionSync(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTx, error) 68 SubmitRawTransactionAsync(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTx, error) 69 SubmitRawTransactionCommit(ctx context.Context, tx []byte) (*tmctypes.ResultBroadcastTxCommit, error) 70 CheckTransaction(ctx context.Context, tx *commandspb.Transaction) (*tmctypes.ResultCheckTx, error) 71 CheckRawTransaction(ctx context.Context, tx []byte) (*tmctypes.ResultCheckTx, error) 72 GetGenesisTime(ctx context.Context) (genesisTime time.Time, err error) 73 GetChainID(ctx context.Context) (chainID string, err error) 74 GetNetworkInfo(ctx context.Context) (netInfo *tmctypes.ResultNetInfo, err error) 75 GetStatus(ctx context.Context) (status *tmctypes.ResultStatus, err error) 76 GetUnconfirmedTxCount(ctx context.Context) (count int, err error) 77 Health() (*tmctypes.ResultHealth, error) 78 } 79 80 type ProofOfWorkParams interface { 81 SpamPoWNumberOfPastBlocks() uint32 82 SpamPoWDifficulty() uint32 83 SpamPoWHashFunction() string 84 SpamPoWNumberOfTxPerBlock() uint32 85 SpamPoWIncreasingDifficulty() bool 86 BlockData() (uint64, string) 87 IsReady() bool 88 } 89 90 type SpamEngine interface { 91 GetSpamStatistics(partyID string) *protoapi.SpamStatistics 92 } 93 94 type PowEngine interface { 95 GetSpamStatistics(partyID string) *protoapi.PoWStatistic 96 } 97 98 // GRPCServer represent the grpc api provided by the vega node. 99 type GRPC struct { 100 Config 101 102 client Blockchain 103 log *logging.Logger 104 srv *grpc.Server 105 stats *stats.Stats 106 timesvc *vegatime.Svc 107 evtfwd EvtForwarder 108 evtService EventService 109 powParams ProofOfWorkParams 110 spamEngine SpamEngine 111 powEngine PowEngine 112 113 // used in order to gracefully close streams 114 ctx context.Context 115 cfunc context.CancelFunc 116 117 core *coreService 118 119 services []func(*grpc.Server) 120 } 121 122 // NewGRPC create a new instance of the GPRC api for the vega node. 123 func NewGRPC( 124 log *logging.Logger, 125 config Config, 126 stats *stats.Stats, 127 client Blockchain, 128 evtfwd EvtForwarder, 129 timeService *vegatime.Svc, 130 eventService *subscribers.Service, 131 powParams ProofOfWorkParams, 132 spamEngine SpamEngine, 133 powEngine PowEngine, 134 ) *GRPC { 135 // setup logger 136 log = log.Named(namedLogger) 137 log.SetLevel(config.Level.Get()) 138 ctx, cfunc := context.WithCancel(context.Background()) 139 140 return &GRPC{ 141 log: log, 142 Config: config, 143 stats: stats, 144 client: client, 145 timesvc: timeService, 146 ctx: ctx, 147 cfunc: cfunc, 148 evtfwd: evtfwd, 149 evtService: eventService, 150 powParams: powParams, 151 spamEngine: spamEngine, 152 powEngine: powEngine, 153 } 154 } 155 156 func (g *GRPC) UpdateProtocolServices( 157 evtforwarder EvtForwarder, 158 timesvc *vegatime.Svc, 159 evtsvc EventService, 160 powParams ProofOfWorkParams, 161 ) { 162 // first save them, in case the core service is not started, 163 // it'll be used later 164 g.evtService = evtsvc 165 g.timesvc = timesvc 166 g.evtfwd = evtforwarder 167 g.powParams = powParams 168 169 if g.core != nil { 170 g.core.UpdateProtocolServices(evtforwarder, timesvc, evtsvc, powParams) 171 } 172 } 173 174 func (g *GRPC) RegisterService(f func(*grpc.Server)) { 175 g.services = append(g.services, f) 176 } 177 178 // ReloadConf update the internal configuration of the GRPC server. 179 func (g *GRPC) ReloadConf(cfg Config) { 180 g.log.Info("reloading configuration") 181 if g.log.GetLevel() != cfg.Level.Get() { 182 g.log.Info("updating log level", 183 logging.String("old", g.log.GetLevel().String()), 184 logging.String("new", cfg.Level.String()), 185 ) 186 g.log.SetLevel(cfg.Level.Get()) 187 } 188 189 // TODO(): not updating the the actual server for now, may need to look at this later 190 // e.g restart the http server on another port or whatever 191 g.Config = cfg 192 g.core.updateConfig(cfg) 193 } 194 195 func remoteAddrInterceptor(log *logging.Logger) grpc.UnaryServerInterceptor { 196 return func( 197 ctx context.Context, 198 req interface{}, 199 info *grpc.UnaryServerInfo, 200 handler grpc.UnaryHandler, 201 ) (resp interface{}, err error) { 202 // first check if the request is forwarded from our restproxy 203 // get the metadata 204 var ip string 205 md, ok := metadata.FromIncomingContext(ctx) 206 if ok { 207 forwardedFor, ok := md["x-forwarded-for"] 208 if ok && len(forwardedFor) > 0 { 209 log.Debug("grpc request x-forwarded-for", 210 logging.String("method", info.FullMethod), 211 logging.String("remote-ip-addr", forwardedFor[0]), 212 ) 213 ip = forwardedFor[0] 214 } 215 } 216 217 // if the request is not forwarded let's get it from the peer infos 218 if len(ip) <= 0 { 219 p, ok := peer.FromContext(ctx) 220 if ok && p != nil { 221 log.Debug("grpc peer client request", 222 logging.String("method", info.FullMethod), 223 logging.String("remote-ip-addr", p.Addr.String())) 224 ip = p.Addr.String() 225 } 226 } 227 228 ctx = vgcontext.WithRemoteIPAddr(ctx, ip) 229 230 // Calls the handler 231 h, err := handler(ctx, req) 232 233 log.Debug("Invoked RPC call", 234 logging.String("method", info.FullMethod), 235 logging.Error(err), 236 ) 237 238 return h, err 239 } 240 } 241 242 // Start start the grpc server. 243 func (g *GRPC) Start() { 244 ip := g.IP 245 port := strconv.Itoa(g.Port) 246 247 g.log.Info("Starting gRPC based API", logging.String("addr", ip), logging.String("port", port)) 248 249 lis, err := net.Listen("tcp", net.JoinHostPort(ip, port)) 250 if err != nil { 251 g.log.Panic("Failure listening on gRPC port", logging.String("port", port), logging.Error(err)) 252 } 253 254 intercept := grpc.UnaryInterceptor(remoteAddrInterceptor(g.log)) 255 g.srv = grpc.NewServer(intercept) 256 257 coreSvc := &coreService{ 258 log: g.log, 259 conf: g.Config, 260 blockchain: g.client, 261 timesvc: g.timesvc, 262 stats: g.stats, 263 evtForwarder: g.evtfwd, 264 eventService: g.evtService, 265 powParams: g.powParams, 266 spamEngine: g.spamEngine, 267 powEngine: g.powEngine, 268 } 269 g.core = coreSvc 270 protoapi.RegisterCoreServiceServer(g.srv, coreSvc) 271 272 for _, f := range g.services { 273 f(g.srv) 274 } 275 276 go g.core.updateNetInfo(g.ctx) 277 278 err = g.srv.Serve(lis) 279 if err != nil { 280 g.log.Panic("Failure serving gRPC API", logging.Error(err)) 281 } 282 } 283 284 // Stop stops the GRPC server. 285 func (g *GRPC) Stop() { 286 if g.srv == nil { 287 return 288 } 289 290 done := make(chan struct{}) 291 go func() { 292 g.log.Info("Gracefully stopping gRPC based API") 293 g.srv.GracefulStop() 294 done <- struct{}{} 295 }() 296 297 select { 298 case <-done: 299 case <-time.After(10 * time.Second): 300 g.log.Info("Force stopping gRPC based API") 301 g.srv.Stop() 302 } 303 }