Skip to content

Commit

Permalink
VM CreateHandlers (#7)
Browse files Browse the repository at this point in the history
* add implementation for method CreateHandlers

* Implement ABCI endpoints (#13)

* implemetn ibc response from lendslidecore

* introduce status rpc method (#14)

Co-authored-by: Ivan Sukach <ivansukach@github.com>

* use cometbft json rpc handler

---------

Co-authored-by: ivansukach <47761294+ivansukach@users.noreply.github.com>
Co-authored-by: Ivan Sukach <ivansukach@github.com>
Co-authored-by: ramil <ramilexe@gmail.com>

* remove landslidecore dependency
add routes for abci_query and abci_info

* update pb

* Add all RPC methods to Route map (#17)

* Add all RPC methods to Route map

* rpc methods signature updated

* MempoolService interface (#18)

* MempoolService interface

* run Github Actions on any PR

---------

Co-authored-by: ramil <ramilexe@gmail.com>

* RPC tests for Health and Status functions

---------

Co-authored-by: ramil <ramilexe@gmail.com>
Co-authored-by: chist100 <44050804+chist100@users.noreply.github.com>
Co-authored-by: ivansukach <47761294+ivansukach@users.noreply.github.com>
Co-authored-by: Ivan Sukach <ivansukach@github.com>
Co-authored-by: Vasyl Naumenko <vnau108@gmail.com>
  • Loading branch information
6 people authored May 1, 2024
1 parent 609c0b6 commit d0e5a92
Show file tree
Hide file tree
Showing 39 changed files with 6,776 additions and 8 deletions.
1 change: 0 additions & 1 deletion .github/workflows/buf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
buf-lint:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_model v0.3.0
github.com/stretchr/testify v1.8.4
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.32.0
)
Expand Down Expand Up @@ -62,6 +63,5 @@ require (
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
101 changes: 101 additions & 0 deletions grpcutils/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package grpcutils

import (
"math"
"time"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
)

const (
// After a duration of this time if the client doesn't see any activity it
// pings the server to see if the transport is still alive.
// If set below 10s, a minimum value of 10s will be used instead.
// grpc-go default infinity
defaultClientKeepAliveTime = 30 * time.Second
// After having pinged for keepalive check, the client waits for a duration
// of Timeout and if no activity is seen even after that the connection is
// closed. grpc-go default 20s
defaultClientKeepAliveTimeOut = 10 * time.Second
// If true, client sends keepalive pings even with no active RPCs. If false,
// when there are no active RPCs, Time and Timeout will be ignored and no
// keepalive pings will be sent. grpc-go default false
defaultPermitWithoutStream = true
// WaitForReady configures the action to take when an RPC is attempted on
// broken connections or unreachable servers. If waitForReady is false and
// the connection is in the TRANSIENT_FAILURE state, the RPC will fail
// immediately. Otherwise, the RPC client will block the call until a
// connection is available (or the call is canceled or times out) and will
// retry the call if it fails due to a transient error. gRPC will not retry
// if data was written to the wire unless the server indicates it did not
// process the data. Please refer to
// https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
//
// gRPC default behavior is to NOT "wait for ready".
defaultWaitForReady = true
)

var DefaultDialOptions = []grpc.DialOption{
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(math.MaxInt),
grpc.MaxCallSendMsgSize(math.MaxInt),
grpc.WaitForReady(defaultWaitForReady),
),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: defaultClientKeepAliveTime,
Timeout: defaultClientKeepAliveTimeOut,
PermitWithoutStream: defaultPermitWithoutStream,
}),
grpc.WithTransportCredentials(insecure.NewCredentials()),
}

// gRPC clients created from this ClientConn will wait forever for the Server to
// become Ready. If you desire a dial timeout ensure context is properly plumbed
// to the client and use context.WithTimeout.
//
// Dial returns a gRPC ClientConn with the dial options as defined by
// DefaultDialOptions. DialOption can also optionally be passed.
func Dial(addr string, opts ...DialOption) (*grpc.ClientConn, error) {
return grpc.Dial("passthrough:///"+addr, newDialOpts(opts...)...)
}

// DialOptions are options which can be applied to a gRPC client in addition to
// the defaults set by DefaultDialOptions.
type DialOptions struct {
opts []grpc.DialOption
}

// append(DefaultDialOptions, ...) will always allocate a new slice and will
// not overwrite any potential data that may have previously been appended to
// DefaultServerOptions https://go.dev/ref/spec#Composite_literals
func newDialOpts(opts ...DialOption) []grpc.DialOption {
d := &DialOptions{opts: DefaultDialOptions}
d.applyOpts(opts)
return d.opts
}

func (d *DialOptions) applyOpts(opts []DialOption) {
for _, opt := range opts {
opt(d)
}
}

type DialOption func(*DialOptions)

// WithChainUnaryInterceptor takes a list of unary client interceptors which
// are added to the dial options.
func WithChainUnaryInterceptor(interceptors ...grpc.UnaryClientInterceptor) DialOption {
return func(d *DialOptions) {
d.opts = append(d.opts, grpc.WithChainUnaryInterceptor(interceptors...))
}
}

// WithChainStreamInterceptor takes a list of stream client interceptors which
// are added to the dial options.
func WithChainStreamInterceptor(interceptors ...grpc.StreamClientInterceptor) DialOption {
return func(d *DialOptions) {
d.opts = append(d.opts, grpc.WithChainStreamInterceptor(interceptors...))
}
}
103 changes: 103 additions & 0 deletions grpcutils/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package grpcutils

import (
"math"
"net"
"time"

"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
)

const (
// MinTime is the minimum amount of time a client should wait before sending
// a keepalive ping. grpc-go default 5 mins
defaultServerKeepAliveMinTime = 5 * time.Second
// After a duration of this time if the server doesn't see any activity it
// pings the client to see if the transport is still alive.
// If set below 1s, a minimum value of 1s will be used instead.
// grpc-go default 2h
defaultServerKeepAliveInterval = 2 * time.Hour
// After having pinged for keepalive check, the server waits for a duration
// of Timeout and if no activity is seen even after that the connection is
// closed. grpc-go default 20s
defaultServerKeepAliveTimeout = 20 * time.Second
)

var DefaultServerOptions = []grpc.ServerOption{
grpc.MaxRecvMsgSize(math.MaxInt),
grpc.MaxSendMsgSize(math.MaxInt),
grpc.MaxConcurrentStreams(math.MaxUint32),
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: defaultServerKeepAliveMinTime,
PermitWithoutStream: defaultPermitWithoutStream,
}),
grpc.KeepaliveParams(keepalive.ServerParameters{
Time: defaultServerKeepAliveInterval,
Timeout: defaultServerKeepAliveTimeout,
}),
}

// NewServer will return a gRPC server with server options as defined by
// DefaultServerOptions. ServerOption can also optionally be passed.
func NewServer(opts ...ServerOption) *grpc.Server {
return grpc.NewServer(newServerOpts(opts)...)
}

type ServerOptions struct {
opts []grpc.ServerOption
}

// append(DefaultServerOptions, ...) will always allocate a new slice and will
// not overwrite any potential data that may have previously been appended to
// DefaultServerOptions https://go.dev/ref/spec#Composite_literals
func newServerOpts(opts []ServerOption) []grpc.ServerOption {
s := &ServerOptions{opts: DefaultServerOptions}
s.applyOpts(opts)
return s.opts
}

func (s *ServerOptions) applyOpts(opts []ServerOption) {
for _, opt := range opts {
opt(s)
}
}

// ServerOption are options which can be applied to a gRPC server in addition to
// the defaults set by DefaultServerOPtions.
type ServerOption func(*ServerOptions)

// WithUnaryInterceptor adds a single unary interceptor to the gRPC server
// options.
func WithUnaryInterceptor(unaryInterceptor grpc.UnaryServerInterceptor) ServerOption {
return func(s *ServerOptions) {
s.opts = append(s.opts, grpc.UnaryInterceptor(unaryInterceptor))
}
}

// WithStreamInterceptor adds a single stream interceptor to the gRPC server
// options.
func WithStreamInterceptor(streamInterceptor grpc.StreamServerInterceptor) ServerOption {
return func(s *ServerOptions) {
s.opts = append(s.opts, grpc.StreamInterceptor(streamInterceptor))
}
}

// NewListener returns a TCP listener listening against the next available port
// on the system bound to localhost.
func NewListener() (net.Listener, error) {
return net.Listen("tcp", "127.0.0.1:")
}

// Serve will start a gRPC server and block until it errors or is shutdown.
func Serve(listener net.Listener, grpcServer *grpc.Server) {
// TODO: While errors will be reported later, it could be useful to somehow
// log this if it is the primary error.
//
// There is nothing to with the error returned by serve here. Later requests
// will propegate their error if they occur.
_ = grpcServer.Serve(listener)

// Similarly, there is nothing to with an error when the listener is closed.
_ = listener.Close()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package closer
package grpcutils

import (
"sync"
Expand Down
102 changes: 102 additions & 0 deletions grpcutils/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package grpcutils

import (
"fmt"
"net/http"
"time"

"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"

httppb "github.com/consideritdone/landslidevm/proto/http"
spb "google.golang.org/genproto/googleapis/rpc/status"
tspb "google.golang.org/protobuf/types/known/timestamppb"
)

func Errorf(code int, tmpl string, args ...interface{}) error {
return GetGRPCErrorFromHTTPResponse(&httppb.HandleSimpleHTTPResponse{
Code: int32(code),
Body: []byte(fmt.Sprintf(tmpl, args...)),
})
}

// GetGRPCErrorFromHTTPRespone takes an HandleSimpleHTTPResponse as input and returns a gRPC error.
func GetGRPCErrorFromHTTPResponse(resp *httppb.HandleSimpleHTTPResponse) error {
a, err := anypb.New(resp)
if err != nil {
return err
}

return status.ErrorProto(&spb.Status{
Code: resp.Code,
Message: string(resp.Body),
Details: []*anypb.Any{a},
})
}

// GetHTTPResponseFromError takes an gRPC error as input and returns a gRPC
// HandleSimpleHTTPResponse.
func GetHTTPResponseFromError(err error) (*httppb.HandleSimpleHTTPResponse, bool) {
s, ok := status.FromError(err)
if !ok {
return nil, false
}

status := s.Proto()
if len(status.Details) != 1 {
return nil, false
}

var resp httppb.HandleSimpleHTTPResponse
if err := anypb.UnmarshalTo(status.Details[0], &resp, proto.UnmarshalOptions{}); err != nil {
return nil, false
}

return &resp, true
}

// GetHTTPHeader takes an http.Header as input and returns a slice of Header.
func GetHTTPHeader(hs http.Header) []*httppb.Element {
result := make([]*httppb.Element, 0, len(hs))
for k, vs := range hs {
result = append(result, &httppb.Element{
Key: k,
Values: vs,
})
}
return result
}

// MergeHTTPHeader takes a slice of Header and merges with http.Header map.
func MergeHTTPHeader(hs []*httppb.Element, header http.Header) {
for _, h := range hs {
header[h.Key] = h.Values
}
}

// TimestampAsTime validates timestamppb timestamp and returns time.Time.
func TimestampAsTime(ts *tspb.Timestamp) (time.Time, error) {
if err := ts.CheckValid(); err != nil {
return time.Time{}, fmt.Errorf("invalid timestamp: %w", err)
}
return ts.AsTime(), nil
}

// TimestampFromTime converts time.Time to a timestamppb timestamp.
func TimestampFromTime(time time.Time) *tspb.Timestamp {
return tspb.New(time)
}

// EnsureValidResponseCode ensures that the response code is valid otherwise it returns 500.
func EnsureValidResponseCode(code int) int {
// Response code outside of this range is invalid and could panic.
// ref. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
if code < 100 || code > 599 {
return http.StatusInternalServerError
}
return code
}
Loading

0 comments on commit d0e5a92

Please sign in to comment.