diff --git a/RELEASES.md b/RELEASES.md index 48884af9e5a9..264f509af390 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,54 @@ # Release Notes +## [v1.7.7](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.7) + +This version is backwards compatible to [v1.7.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0). It is optional, but encouraged. + +### Networking + +- Refactored the networking library to track potential peers by nodeID rather than IP. +- Separated peer connections from the mesh network implementation to simplify testing. +- Fixed duplicate `Connected` messages bug. +- Supported establishing outbound connections with peers reporting different inbound and outbound IPs. + +### Database + +- Disabled seek compaction in leveldb by default. + +### GRPC + +- Increased protocol version, this requires all plugin definitions to update their communication dependencies. +- Merged services to be served using the same server when possible. +- Implemented a fast path for simple HTTP requests. +- Removed duplicated message definitions. +- Improved error reporting around invalid plugins. + +### Coreth + +- Optimized FeeHistory API. +- Added protection to prevent accidental corruption of archival node trie index. +- Added capability to restore complete trie index on best effort basis. +- Rounded up fastcache sizes to utilize all mmap'd memory in chunks of 64MB. + +### Configs + +- Removed `--inbound-connection-throttling-max-recent` +- Renamed `--network-peer-list-size` to `--network-peer-list-num-validator-ips` +- Removed `--network-peer-list-gossip-size` +- Removed `--network-peer-list-staker-gossip-fraction` +- Added `--network-peer-list-validator-gossip-size` +- Added `--network-peer-list-non-validator-gossip-size` +- Removed `--network-get-version-timeout` +- Removed `--benchlist-peer-summary-enabled` +- Removed `--peer-alias-timeout` + +### Miscellaneous + +- Fixed error reporting when making Avalanche chains that did not manually specify a primary alias. +- Added beacon utils for easier programmatic handling of beacon nodes. +- Resolved the default log directory on initialization to avoid additional error handling. +- Added support to the chain state module to specify an arbitrary new accepted block. + ## [v1.7.6](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.6) This version is backwards compatible to [v1.7.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.7.0). It is optional, but encouraged. diff --git a/api/buf.yaml b/api/buf.yaml index 613613cefea8..3c22545276a7 100644 --- a/api/buf.yaml +++ b/api/buf.yaml @@ -17,6 +17,7 @@ lint: # TODO: how will fixing this affect functionality. Multiple fields are used as the request # or response type for multiple RPCs - galiasreaderproto/aliasreader.proto + - gconnproto/conn.proto # allows RPC requests or responses to be google.protobuf.Empty messages. This can be set if you # want to allow messages to be void forever, that is they will never take any parameters. rpc_allow_google_protobuf_empty_requests: true diff --git a/api/gconnproto/conn.proto b/api/gconnproto/conn.proto index f4a123e14a7d..75b7b0a92e43 100644 --- a/api/gconnproto/conn.proto +++ b/api/gconnproto/conn.proto @@ -1,54 +1,56 @@ syntax = "proto3"; package gconnproto; option go_package = "github.com/ava-labs/avalanchego/api/gconnproto"; +import "google/protobuf/empty.proto"; + +// Conn is a net.Conn see: https://pkg.go.dev/net#Conn +service Conn { + // Read reads data from the connection. + rpc Read(ReadRequest) returns (ReadResponse); + // Write writes data to the connection. + rpc Write(WriteRequest) returns (WriteResponse); + // Close closes the connection. + rpc Close(google.protobuf.Empty) returns (google.protobuf.Empty); + // SetDeadline sets the read and write deadlines associated + // with the connection. + rpc SetDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + rpc SetReadDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + rpc SetWriteDeadline(SetDeadlineRequest) returns (google.protobuf.Empty); +} message ReadRequest { + // length of the request in bytes int32 length = 1; } message ReadResponse { + // read is the payload in bytes bytes read = 1; + // error is an error message string error = 2; + // errored is true if an error has been set bool errored = 3; } message WriteRequest { + // payload is the write request in bytes bytes payload = 1; } message WriteResponse { + // length of the response in bytes int32 length = 1; + // error is an error message string error = 2; + // errored is true if an error has been set bool errored = 3; } -message CloseRequest {} - -message CloseResponse {} - message SetDeadlineRequest { + // time represents an instant in time in bytes bytes time = 1; } - -message SetDeadlineResponse {} - -message SetReadDeadlineRequest { - bytes time = 1; -} - -message SetReadDeadlineResponse {} - -message SetWriteDeadlineRequest { - bytes time = 1; -} - -message SetWriteDeadlineResponse {} - -service Conn { - rpc Read(ReadRequest) returns (ReadResponse); - rpc Write(WriteRequest) returns (WriteResponse); - rpc Close(CloseRequest) returns (CloseResponse); - rpc SetDeadline(SetDeadlineRequest) returns (SetDeadlineResponse); - rpc SetReadDeadline(SetReadDeadlineRequest) returns (SetReadDeadlineResponse); - rpc SetWriteDeadline(SetWriteDeadlineRequest) returns (SetWriteDeadlineResponse); -} diff --git a/api/ghttpproto/http.proto b/api/ghttpproto/http.proto index b4d916251082..3ecbf3e38e8e 100644 --- a/api/ghttpproto/http.proto +++ b/api/ghttpproto/http.proto @@ -1,80 +1,171 @@ syntax = "proto3"; package ghttpproto; option go_package = "github.com/ava-labs/avalanchego/api/ghttpproto"; +import "google/protobuf/empty.proto"; -message Userinfo { - string username = 1; - string password = 2; - bool password_set = 3; +service HTTP { + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + rpc Handle(HTTPRequest) returns (google.protobuf.Empty); + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + rpc HandleSimple(HandleSimpleHTTPRequest) returns (HandleSimpleHTTPResponse); } +// URL is a net.URL see: https://pkg.go.dev/net/url#URL message URL { + // scheme is the url scheme name string scheme = 1; + // opaque is encoded opaque data string opaque = 2; + // user is username and password information Userinfo user = 3; + // host can be in the format host or host:port string host = 4; + // path (relative paths may omit leading slash) string path = 5; + // raw_path is encoded path hint (see EscapedPath method) string raw_path = 6; + // force is append a query ('?') even if RawQuery is empty bool force_query = 7; + // raw_query is encoded query values, without '?' string raw_query = 8; + // fragment is fragment for references, without '#' string fragment = 9; } +// UserInfo is net.Userinfo see: https://pkg.go.dev/net/url#Userinfo +message Userinfo { + // username is the username for the user + string username = 1; + // password is the password for the user + string password = 2; + // password_set is a boolean which is true if the passord is set + bool password_set = 3; +} + message Element { + // key is a element key in a key value pair string key = 1; + // values are a list of strings coresponding to the key repeated string values = 2; } message Certificates { + // cert is the certificate body repeated bytes cert = 1; } +// ConnectionState is tls.ConnectionState see: https://pkg.go.dev/crypto/tls#ConnectionState message ConnectionState { + // reserved fields ids have been removed and should not be reused + reserved 6, 12; + // version is the TLS version used by the connection (e.g. VersionTLS12) uint32 version = 1; + // handshake_complete is true if the handshake has concluded bool handshake_complete = 2; + // did_resume is true if this connection was successfully resumed from a + // previous session with a session ticket or similar mechanism bool did_resume = 3; + // cipher_suite is the cipher suite negotiated for the connection uint32 cipher_suite = 4; + // negotiated_protocol is the application protocol negotiated with ALPN string negotiated_protocol = 5; - bool negotiated_protocol_is_mutual = 6; + // server_name is the value of the Server Name Indication extension sent by + // the client string server_name = 7; + // peer_certificates are the parsed certificates sent by the peer, in the + // order in which they were sent Certificates peer_certificates = 8; + // verified_chains is a list of one or more chains where the first element is + // PeerCertificates[0] and the last element is from Config.RootCAs (on the + // client side) or Config.ClientCAs (on the server side). repeated Certificates verified_chains = 9; + // signed_certificate_timestamps is a list of SCTs provided by the peer + // through the TLS handshake for the leaf certificate, if any repeated bytes signed_certificate_timestamps = 10; + // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) + // response provided by the peer for the leaf certificate, if any. bytes ocsp_response = 11; - bytes tls_unique = 12; } +// Request is an http.Request see: https://pkg.go.dev/net/http#Request message Request { + // method specifies the HTTP method (GET, POST, PUT, etc.) string method = 1; + // url specifies either the URI being requested (for server requests) + // or the URL to access (for client requests) URL url = 2; + // proto is the protocol version for incoming server requests string proto = 3; + // proto_major is the major version int32 proto_major = 4; + // proto_minor is the minor version int32 proto_minor = 5; + // header contains the request header fields either received + // by the server or to be sent by the client repeated Element header = 6; - uint32 body = 7; // server ID + // body is the request payload in bytes + bytes body = 7; + // content_length records the length of the associated content int64 content_length = 8; + // transfer_encoding lists the transfer encodings from outermost to + // innermost repeated string transfer_encoding = 9; + // host specifies the host on which the URL is sought string host = 10; + // form contains the parsed form data, including both the URL + // field's query parameters and the PATCH, POST, or PUT form data repeated Element form = 11; + // post_form contains the parsed form data from PATCH, POST + // or PUT body parameters repeated Element post_form = 12; + // trailer_keys specifies additional headers that are sent after the request repeated string trailer_keys = 13; + // remote_addr allows HTTP servers and other software to record + // the network address that sent the request string remote_addr = 14; + // request_uri is the unmodified request-target string request_uri = 15; + // tls connection state ConnectionState tls = 16; } message ResponseWriter { - uint32 id = 1; // server ID + // id correlates to a stream id of the gRPC server hosting the Writer service + uint32 id = 1; + // header returns the header map that will be sent by + // WriteHeader. repeated Element header = 2; } message HTTPRequest { + // response_writer is used by an HTTP handler to construct an HTTP response ResponseWriter response_writer = 1; + // request is an http request Request request = 2; } -message HTTPResponse {} +message HandleSimpleHTTPRequest { + // method specifies the HTTP method (GET, POST, PUT, etc.) + string method = 1; + // url specifies either the URI being requested + string url = 2; + // headers contains the request header fields either received + // by the server or to be sent by the client + repeated Element headers = 3; + // body is the request payload in bytes + bytes body = 4; +} -service HTTP { - rpc Handle(HTTPRequest) returns (HTTPResponse); +message HandleSimpleHTTPResponse { + // code is the response code + int32 code = 1; + // headers contains the request header fields either received + // by the server or to be sent by the client + repeated Element headers = 2; + // body is the response payload in bytes + bytes body = 3; } diff --git a/api/greadcloserproto/readcloser.proto b/api/greadcloserproto/readcloser.proto deleted file mode 100644 index 383a3602973a..000000000000 --- a/api/greadcloserproto/readcloser.proto +++ /dev/null @@ -1,22 +0,0 @@ -syntax = "proto3"; -package greadcloserproto; -option go_package = "github.com/ava-labs/avalanchego/api/greadcloserproto"; - -message ReadRequest { - int32 length = 1; -} - -message ReadResponse { - bytes read = 1; - string error = 2; - bool errored = 3; -} - -message CloseRequest {} - -message CloseResponse {} - -service Reader { - rpc Read(ReadRequest) returns (ReadResponse); - rpc Close(CloseRequest) returns (CloseResponse); -} diff --git a/api/greaderproto/reader.proto b/api/greaderproto/reader.proto index 4f44b87deaed..d312d02b4f48 100644 --- a/api/greaderproto/reader.proto +++ b/api/greaderproto/reader.proto @@ -2,16 +2,21 @@ syntax = "proto3"; package greaderproto; option go_package = "github.com/ava-labs/avalanchego/api/greaderproto"; +// Reader is an io.Reader see: https://pkg.go.dev/io#Reader +service Reader { + rpc Read(ReadRequest) returns (ReadResponse); +} + message ReadRequest { + // length is the request in bytes int32 length = 1; } message ReadResponse { + // read is the payload in bytes bytes read = 1; + // error is an error message string error = 2; + // errored is true if an error has been set bool errored = 3; } - -service Reader { - rpc Read(ReadRequest) returns (ReadResponse); -} diff --git a/api/gresponsewriterproto/responsewriter.proto b/api/gresponsewriterproto/responsewriter.proto index 82c68703415d..10472031f859 100644 --- a/api/gresponsewriterproto/responsewriter.proto +++ b/api/gresponsewriterproto/responsewriter.proto @@ -1,47 +1,59 @@ syntax = "proto3"; package gresponsewriterproto; option go_package = "github.com/ava-labs/avalanchego/api/gresponsewriterproto"; +import "google/protobuf/empty.proto"; + +// Writer is an http.ResponseWriter see: https://pkg.go.dev/net/http#ResponseWriter +service Writer { + // Write writes the data to the connection as part of an HTTP reply + rpc Write(WriteRequest) returns (WriteResponse); + // WriteHeader sends an HTTP response header with the provided + // status code + rpc WriteHeader(WriteHeaderRequest) returns (google.protobuf.Empty); + // Flush is a no-op + rpc Flush(google.protobuf.Empty) returns (google.protobuf.Empty); + // Hijack lets the caller take over the connection + rpc Hijack(google.protobuf.Empty) returns (HijackResponse); +} message Header { + // key is a element key in a key value pair string key = 1; + // values are a list of strings coresponding to the key repeated string values = 2; } message WriteRequest { + // headers represents the key-value pairs in an HTTP header repeated Header headers = 1; + // payload is the write request in bytes bytes payload = 2; } message WriteResponse { + // written is the number of bytes written in body int32 written = 1; } message WriteHeaderRequest { + // headers represents the key-value pairs in an HTTP header repeated Header headers = 1; + // status_code must be a valid HTTP 1xx-5xx status code int32 status_code = 2; } -message WriteHeaderResponse {} - -message FlushRequest {} - -message FlushResponse {} - -message HijackRequest {} - message HijackResponse { - uint32 conn_server = 1; + // reserved fields ids have been removed and should not be reused + reserved 1, 6, 7; string local_network = 2; + // local_string is string form of address string local_string = 3; + // remote_network is the name of the network (for example, "tcp", "udp") string remote_network = 4; + // remote_string is string form of address string remote_string = 5; - uint32 reader_server = 6; - uint32 writer_server = 7; -} - -service Writer { - rpc Write(WriteRequest) returns (WriteResponse); - rpc WriteHeader(WriteHeaderRequest) returns (WriteHeaderResponse); - rpc Flush(FlushRequest) returns (FlushResponse); - rpc Hijack(HijackRequest) returns (HijackResponse); + // conn_read_writer_server is the stream id of the gRPC server + // serving the Conn, Reader and Writer services which facilitate + // Hijacking, + uint32 conn_read_writer_server = 8; } diff --git a/api/gwriterproto/writer.proto b/api/gwriterproto/writer.proto index f89ed486a7b5..5213860faa7e 100644 --- a/api/gwriterproto/writer.proto +++ b/api/gwriterproto/writer.proto @@ -2,16 +2,22 @@ syntax = "proto3"; package gwriterproto; option go_package = "github.com/ava-labs/avalanchego/api/gwriterproto"; +// Writer see: io.Writer https://pkg.go.dev/io#Writer +service Writer { + // Write writes len(p) bytes from p to the underlying data stream. + rpc Write(WriteRequest) returns (WriteResponse); +} + message WriteRequest { + // payload is the write request in bytes bytes payload = 1; } message WriteResponse { + // written is the length of payload in bytes int32 written = 1; + // error is an error message string error = 2; + // errored is true if an error has been set bool errored = 3; } - -service Writer { - rpc Write(WriteRequest) returns (WriteResponse); -} diff --git a/api/info/client.go b/api/info/client.go index af875ad15514..0485a2efab11 100644 --- a/api/info/client.go +++ b/api/info/client.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/network" "github.com/ava-labs/avalanchego/utils/rpc" ) @@ -22,7 +21,7 @@ type Client interface { GetNetworkID(context.Context) (uint32, error) GetNetworkName(context.Context) (string, error) GetBlockchainID(context.Context, string) (ids.ID, error) - Peers(context.Context) ([]network.PeerInfo, error) + Peers(context.Context) ([]Peer, error) IsBootstrapped(context.Context, string) (bool, error) GetTxFee(context.Context) (*GetTxFeeResponse, error) Uptime(context.Context) (*UptimeResponse, error) @@ -79,7 +78,7 @@ func (c *client) GetBlockchainID(ctx context.Context, alias string) (ids.ID, err return res.BlockchainID, err } -func (c *client) Peers(ctx context.Context) ([]network.PeerInfo, error) { +func (c *client) Peers(ctx context.Context) ([]Peer, error) { res := &PeersReply{} err := c.requester.SendRequest(ctx, "peers", struct{}{}, res) return res.Peers, err diff --git a/api/info/service.go b/api/info/service.go index 9c9b2965c13c..65b3610167e2 100644 --- a/api/info/service.go +++ b/api/info/service.go @@ -13,8 +13,11 @@ import ( "github.com/ava-labs/avalanchego/chains" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network" + "github.com/ava-labs/avalanchego/network/peer" "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/networking/benchlist" "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/json" "github.com/ava-labs/avalanchego/utils/logging" @@ -31,11 +34,13 @@ var ( type Info struct { Parameters log logging.Logger + myIP *utils.DynamicIPDesc networking network.Network chainManager chains.Manager vmManager vms.Manager versionParser version.ApplicationParser validators validators.Set + benchlist benchlist.Manager } type Parameters struct { @@ -55,9 +60,11 @@ func NewService( log logging.Logger, chainManager chains.Manager, vmManager vms.Manager, + myIP *utils.DynamicIPDesc, network network.Network, versionParser version.ApplicationParser, validators validators.Set, + benchlist benchlist.Manager, ) (*common.HTTPHandler, error) { newServer := rpc.NewServer() codec := json.NewCodec() @@ -68,9 +75,11 @@ func NewService( log: log, chainManager: chainManager, vmManager: vmManager, + myIP: myIP, networking: network, versionParser: versionParser, validators: validators, + benchlist: benchlist, }, "info"); err != nil { return nil, err } @@ -128,7 +137,7 @@ type GetNodeIPReply struct { func (service *Info) GetNodeIP(_ *http.Request, _ *struct{}, reply *GetNodeIPReply) error { service.log.Debug("Info: GetNodeIP called") - reply.IP = service.networking.IP().String() + reply.IP = service.myIP.IP().String() return nil } @@ -177,12 +186,18 @@ type PeersArgs struct { NodeIDs []string `json:"nodeIDs"` } +type Peer struct { + peer.Info + + Benched []ids.ID `json:"benched"` +} + // PeersReply are the results from calling Peers type PeersReply struct { // Number of elements in [Peers] NumPeers json.Uint64 `json:"numPeers"` // Each element is a peer - Peers []network.PeerInfo `json:"peers"` + Peers []Peer `json:"peers"` } // Peers returns the list of current validators @@ -197,7 +212,21 @@ func (service *Info) Peers(_ *http.Request, args *PeersArgs, reply *PeersReply) nodeIDs = append(nodeIDs, nID) } - reply.Peers = service.networking.Peers(nodeIDs) + peers := service.networking.PeerInfo(nodeIDs) + peerInfo := make([]Peer, len(peers)) + for i, peer := range peers { + nodeID, err := ids.ShortFromPrefixedString(peer.ID, constants.NodeIDPrefix) + if err != nil { + return err + } + + peerInfo[i] = Peer{ + Info: peer, + Benched: service.benchlist.GetBenched(nodeID), + } + } + + reply.Peers = peerInfo reply.NumPeers = json.Uint64(len(reply.Peers)) return nil } diff --git a/api/proto/gconnproto/conn.pb.go b/api/proto/gconnproto/conn.pb.go index f603386db8e2..394b24ffb797 100644 --- a/api/proto/gconnproto/conn.pb.go +++ b/api/proto/gconnproto/conn.pb.go @@ -9,6 +9,7 @@ package gconnproto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -25,6 +26,7 @@ type ReadRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // length of the request in bytes Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` } @@ -72,9 +74,12 @@ type ReadResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` + // read is the payload in bytes + Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` + // error is an error message + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // errored is true if an error has been set + Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` } func (x *ReadResponse) Reset() { @@ -135,6 +140,7 @@ type WriteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // payload is the write request in bytes Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` } @@ -182,9 +188,12 @@ type WriteResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` + // length of the response in bytes + Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` + // error is an error message + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // errored is true if an error has been set + Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` } func (x *WriteResponse) Reset() { @@ -240,94 +249,19 @@ func (x *WriteResponse) GetErrored() bool { return false } -type CloseRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CloseRequest) Reset() { - *x = CloseRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseRequest) ProtoMessage() {} - -func (x *CloseRequest) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseRequest.ProtoReflect.Descriptor instead. -func (*CloseRequest) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{4} -} - -type CloseResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CloseResponse) Reset() { - *x = CloseResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseResponse) ProtoMessage() {} - -func (x *CloseResponse) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseResponse.ProtoReflect.Descriptor instead. -func (*CloseResponse) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{5} -} - type SetDeadlineRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // time represents an instant in time in bytes Time []byte `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` } func (x *SetDeadlineRequest) Reset() { *x = SetDeadlineRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[6] + mi := &file_gconnproto_conn_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -340,7 +274,7 @@ func (x *SetDeadlineRequest) String() string { func (*SetDeadlineRequest) ProtoMessage() {} func (x *SetDeadlineRequest) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[6] + mi := &file_gconnproto_conn_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -353,7 +287,7 @@ func (x *SetDeadlineRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetDeadlineRequest.ProtoReflect.Descriptor instead. func (*SetDeadlineRequest) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{6} + return file_gconnproto_conn_proto_rawDescGZIP(), []int{4} } func (x *SetDeadlineRequest) GetTime() []byte { @@ -363,283 +297,61 @@ func (x *SetDeadlineRequest) GetTime() []byte { return nil } -type SetDeadlineResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SetDeadlineResponse) Reset() { - *x = SetDeadlineResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetDeadlineResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetDeadlineResponse) ProtoMessage() {} - -func (x *SetDeadlineResponse) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetDeadlineResponse.ProtoReflect.Descriptor instead. -func (*SetDeadlineResponse) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{7} -} - -type SetReadDeadlineRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Time []byte `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` -} - -func (x *SetReadDeadlineRequest) Reset() { - *x = SetReadDeadlineRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetReadDeadlineRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetReadDeadlineRequest) ProtoMessage() {} - -func (x *SetReadDeadlineRequest) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetReadDeadlineRequest.ProtoReflect.Descriptor instead. -func (*SetReadDeadlineRequest) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{8} -} - -func (x *SetReadDeadlineRequest) GetTime() []byte { - if x != nil { - return x.Time - } - return nil -} - -type SetReadDeadlineResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SetReadDeadlineResponse) Reset() { - *x = SetReadDeadlineResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetReadDeadlineResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetReadDeadlineResponse) ProtoMessage() {} - -func (x *SetReadDeadlineResponse) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetReadDeadlineResponse.ProtoReflect.Descriptor instead. -func (*SetReadDeadlineResponse) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{9} -} - -type SetWriteDeadlineRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Time []byte `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` -} - -func (x *SetWriteDeadlineRequest) Reset() { - *x = SetWriteDeadlineRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetWriteDeadlineRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetWriteDeadlineRequest) ProtoMessage() {} - -func (x *SetWriteDeadlineRequest) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetWriteDeadlineRequest.ProtoReflect.Descriptor instead. -func (*SetWriteDeadlineRequest) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{10} -} - -func (x *SetWriteDeadlineRequest) GetTime() []byte { - if x != nil { - return x.Time - } - return nil -} - -type SetWriteDeadlineResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SetWriteDeadlineResponse) Reset() { - *x = SetWriteDeadlineResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gconnproto_conn_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetWriteDeadlineResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetWriteDeadlineResponse) ProtoMessage() {} - -func (x *SetWriteDeadlineResponse) ProtoReflect() protoreflect.Message { - mi := &file_gconnproto_conn_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetWriteDeadlineResponse.ProtoReflect.Descriptor instead. -func (*SetWriteDeadlineResponse) Descriptor() ([]byte, []int) { - return file_gconnproto_conn_proto_rawDescGZIP(), []int{11} -} - var File_gconnproto_conn_proto protoreflect.FileDescriptor var file_gconnproto_conn_proto_rawDesc = []byte{ 0x0a, 0x15, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x52, 0x0a, 0x0c, 0x52, 0x65, - 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, - 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x14, + 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x25, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x52, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x61, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x28, 0x0a, 0x0c, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x57, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x28, - 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x57, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, - 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x28, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x15, 0x0a, 0x13, - 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x44, 0x65, - 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x44, 0x65, 0x61, 0x64, - 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x17, - 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x53, - 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc8, 0x03, 0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x6e, - 0x12, 0x39, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x44, 0x65, - 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x52, 0x65, - 0x61, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x6f, - 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x44, - 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x61, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, - 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x65, 0x61, - 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, - 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0x12, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x32, 0x96, 0x03, 0x0a, 0x04, 0x43, 0x6f, 0x6e, + 0x6e, 0x12, 0x39, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x6f, 0x6e, + 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x45, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, + 0x6e, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x0f, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x61, 0x64, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1e, 0x2e, + 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, + 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, + 0x65, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x6f, 0x6e, + 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, + 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x63, 0x6f, 0x6e, 0x6e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -654,39 +366,33 @@ func file_gconnproto_conn_proto_rawDescGZIP() []byte { return file_gconnproto_conn_proto_rawDescData } -var file_gconnproto_conn_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_gconnproto_conn_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_gconnproto_conn_proto_goTypes = []interface{}{ - (*ReadRequest)(nil), // 0: gconnproto.ReadRequest - (*ReadResponse)(nil), // 1: gconnproto.ReadResponse - (*WriteRequest)(nil), // 2: gconnproto.WriteRequest - (*WriteResponse)(nil), // 3: gconnproto.WriteResponse - (*CloseRequest)(nil), // 4: gconnproto.CloseRequest - (*CloseResponse)(nil), // 5: gconnproto.CloseResponse - (*SetDeadlineRequest)(nil), // 6: gconnproto.SetDeadlineRequest - (*SetDeadlineResponse)(nil), // 7: gconnproto.SetDeadlineResponse - (*SetReadDeadlineRequest)(nil), // 8: gconnproto.SetReadDeadlineRequest - (*SetReadDeadlineResponse)(nil), // 9: gconnproto.SetReadDeadlineResponse - (*SetWriteDeadlineRequest)(nil), // 10: gconnproto.SetWriteDeadlineRequest - (*SetWriteDeadlineResponse)(nil), // 11: gconnproto.SetWriteDeadlineResponse + (*ReadRequest)(nil), // 0: gconnproto.ReadRequest + (*ReadResponse)(nil), // 1: gconnproto.ReadResponse + (*WriteRequest)(nil), // 2: gconnproto.WriteRequest + (*WriteResponse)(nil), // 3: gconnproto.WriteResponse + (*SetDeadlineRequest)(nil), // 4: gconnproto.SetDeadlineRequest + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_gconnproto_conn_proto_depIdxs = []int32{ - 0, // 0: gconnproto.Conn.Read:input_type -> gconnproto.ReadRequest - 2, // 1: gconnproto.Conn.Write:input_type -> gconnproto.WriteRequest - 4, // 2: gconnproto.Conn.Close:input_type -> gconnproto.CloseRequest - 6, // 3: gconnproto.Conn.SetDeadline:input_type -> gconnproto.SetDeadlineRequest - 8, // 4: gconnproto.Conn.SetReadDeadline:input_type -> gconnproto.SetReadDeadlineRequest - 10, // 5: gconnproto.Conn.SetWriteDeadline:input_type -> gconnproto.SetWriteDeadlineRequest - 1, // 6: gconnproto.Conn.Read:output_type -> gconnproto.ReadResponse - 3, // 7: gconnproto.Conn.Write:output_type -> gconnproto.WriteResponse - 5, // 8: gconnproto.Conn.Close:output_type -> gconnproto.CloseResponse - 7, // 9: gconnproto.Conn.SetDeadline:output_type -> gconnproto.SetDeadlineResponse - 9, // 10: gconnproto.Conn.SetReadDeadline:output_type -> gconnproto.SetReadDeadlineResponse - 11, // 11: gconnproto.Conn.SetWriteDeadline:output_type -> gconnproto.SetWriteDeadlineResponse - 6, // [6:12] is the sub-list for method output_type - 0, // [0:6] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: gconnproto.Conn.Read:input_type -> gconnproto.ReadRequest + 2, // 1: gconnproto.Conn.Write:input_type -> gconnproto.WriteRequest + 5, // 2: gconnproto.Conn.Close:input_type -> google.protobuf.Empty + 4, // 3: gconnproto.Conn.SetDeadline:input_type -> gconnproto.SetDeadlineRequest + 4, // 4: gconnproto.Conn.SetReadDeadline:input_type -> gconnproto.SetDeadlineRequest + 4, // 5: gconnproto.Conn.SetWriteDeadline:input_type -> gconnproto.SetDeadlineRequest + 1, // 6: gconnproto.Conn.Read:output_type -> gconnproto.ReadResponse + 3, // 7: gconnproto.Conn.Write:output_type -> gconnproto.WriteResponse + 5, // 8: gconnproto.Conn.Close:output_type -> google.protobuf.Empty + 5, // 9: gconnproto.Conn.SetDeadline:output_type -> google.protobuf.Empty + 5, // 10: gconnproto.Conn.SetReadDeadline:output_type -> google.protobuf.Empty + 5, // 11: gconnproto.Conn.SetWriteDeadline:output_type -> google.protobuf.Empty + 6, // [6:12] is the sub-list for method output_type + 0, // [0:6] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_gconnproto_conn_proto_init() } @@ -744,30 +450,6 @@ func file_gconnproto_conn_proto_init() { } } file_gconnproto_conn_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetDeadlineRequest); i { case 0: return &v.state @@ -779,66 +461,6 @@ func file_gconnproto_conn_proto_init() { return nil } } - file_gconnproto_conn_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetDeadlineResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetReadDeadlineRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetReadDeadlineResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetWriteDeadlineRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gconnproto_conn_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetWriteDeadlineResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -846,7 +468,7 @@ func file_gconnproto_conn_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gconnproto_conn_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/api/proto/gconnproto/conn_grpc.pb.go b/api/proto/gconnproto/conn_grpc.pb.go index b641ea375ba2..3fd9bdb7c764 100644 --- a/api/proto/gconnproto/conn_grpc.pb.go +++ b/api/proto/gconnproto/conn_grpc.pb.go @@ -11,6 +11,7 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -22,12 +23,21 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ConnClient interface { + // Read reads data from the connection. Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) + // Write writes data to the connection. Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) - Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) - SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*SetDeadlineResponse, error) - SetReadDeadline(ctx context.Context, in *SetReadDeadlineRequest, opts ...grpc.CallOption) (*SetReadDeadlineResponse, error) - SetWriteDeadline(ctx context.Context, in *SetWriteDeadlineRequest, opts ...grpc.CallOption) (*SetWriteDeadlineResponse, error) + // Close closes the connection. + Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetDeadline sets the read and write deadlines associated + // with the connection. + SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + SetReadDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + SetWriteDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } type connClient struct { @@ -56,8 +66,8 @@ func (c *connClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc.C return out, nil } -func (c *connClient) Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) { - out := new(CloseResponse) +func (c *connClient) Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gconnproto.Conn/Close", in, out, opts...) if err != nil { return nil, err @@ -65,8 +75,8 @@ func (c *connClient) Close(ctx context.Context, in *CloseRequest, opts ...grpc.C return out, nil } -func (c *connClient) SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*SetDeadlineResponse, error) { - out := new(SetDeadlineResponse) +func (c *connClient) SetDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gconnproto.Conn/SetDeadline", in, out, opts...) if err != nil { return nil, err @@ -74,8 +84,8 @@ func (c *connClient) SetDeadline(ctx context.Context, in *SetDeadlineRequest, op return out, nil } -func (c *connClient) SetReadDeadline(ctx context.Context, in *SetReadDeadlineRequest, opts ...grpc.CallOption) (*SetReadDeadlineResponse, error) { - out := new(SetReadDeadlineResponse) +func (c *connClient) SetReadDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gconnproto.Conn/SetReadDeadline", in, out, opts...) if err != nil { return nil, err @@ -83,8 +93,8 @@ func (c *connClient) SetReadDeadline(ctx context.Context, in *SetReadDeadlineReq return out, nil } -func (c *connClient) SetWriteDeadline(ctx context.Context, in *SetWriteDeadlineRequest, opts ...grpc.CallOption) (*SetWriteDeadlineResponse, error) { - out := new(SetWriteDeadlineResponse) +func (c *connClient) SetWriteDeadline(ctx context.Context, in *SetDeadlineRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gconnproto.Conn/SetWriteDeadline", in, out, opts...) if err != nil { return nil, err @@ -96,12 +106,21 @@ func (c *connClient) SetWriteDeadline(ctx context.Context, in *SetWriteDeadlineR // All implementations must embed UnimplementedConnServer // for forward compatibility type ConnServer interface { + // Read reads data from the connection. Read(context.Context, *ReadRequest) (*ReadResponse, error) + // Write writes data to the connection. Write(context.Context, *WriteRequest) (*WriteResponse, error) - Close(context.Context, *CloseRequest) (*CloseResponse, error) - SetDeadline(context.Context, *SetDeadlineRequest) (*SetDeadlineResponse, error) - SetReadDeadline(context.Context, *SetReadDeadlineRequest) (*SetReadDeadlineResponse, error) - SetWriteDeadline(context.Context, *SetWriteDeadlineRequest) (*SetWriteDeadlineResponse, error) + // Close closes the connection. + Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + // SetDeadline sets the read and write deadlines associated + // with the connection. + SetDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + SetReadDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + SetWriteDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) mustEmbedUnimplementedConnServer() } @@ -115,16 +134,16 @@ func (UnimplementedConnServer) Read(context.Context, *ReadRequest) (*ReadRespons func (UnimplementedConnServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") } -func (UnimplementedConnServer) Close(context.Context, *CloseRequest) (*CloseResponse, error) { +func (UnimplementedConnServer) Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") } -func (UnimplementedConnServer) SetDeadline(context.Context, *SetDeadlineRequest) (*SetDeadlineResponse, error) { +func (UnimplementedConnServer) SetDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SetDeadline not implemented") } -func (UnimplementedConnServer) SetReadDeadline(context.Context, *SetReadDeadlineRequest) (*SetReadDeadlineResponse, error) { +func (UnimplementedConnServer) SetReadDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SetReadDeadline not implemented") } -func (UnimplementedConnServer) SetWriteDeadline(context.Context, *SetWriteDeadlineRequest) (*SetWriteDeadlineResponse, error) { +func (UnimplementedConnServer) SetWriteDeadline(context.Context, *SetDeadlineRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SetWriteDeadline not implemented") } func (UnimplementedConnServer) mustEmbedUnimplementedConnServer() {} @@ -177,7 +196,7 @@ func _Conn_Write_Handler(srv interface{}, ctx context.Context, dec func(interfac } func _Conn_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CloseRequest) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -189,7 +208,7 @@ func _Conn_Close_Handler(srv interface{}, ctx context.Context, dec func(interfac FullMethod: "/gconnproto.Conn/Close", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConnServer).Close(ctx, req.(*CloseRequest)) + return srv.(ConnServer).Close(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } @@ -213,7 +232,7 @@ func _Conn_SetDeadline_Handler(srv interface{}, ctx context.Context, dec func(in } func _Conn_SetReadDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetReadDeadlineRequest) + in := new(SetDeadlineRequest) if err := dec(in); err != nil { return nil, err } @@ -225,13 +244,13 @@ func _Conn_SetReadDeadline_Handler(srv interface{}, ctx context.Context, dec fun FullMethod: "/gconnproto.Conn/SetReadDeadline", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConnServer).SetReadDeadline(ctx, req.(*SetReadDeadlineRequest)) + return srv.(ConnServer).SetReadDeadline(ctx, req.(*SetDeadlineRequest)) } return interceptor(ctx, in, info, handler) } func _Conn_SetWriteDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetWriteDeadlineRequest) + in := new(SetDeadlineRequest) if err := dec(in); err != nil { return nil, err } @@ -243,7 +262,7 @@ func _Conn_SetWriteDeadline_Handler(srv interface{}, ctx context.Context, dec fu FullMethod: "/gconnproto.Conn/SetWriteDeadline", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ConnServer).SetWriteDeadline(ctx, req.(*SetWriteDeadlineRequest)) + return srv.(ConnServer).SetWriteDeadline(ctx, req.(*SetDeadlineRequest)) } return interceptor(ctx, in, info, handler) } diff --git a/api/proto/ghttpproto/http.pb.go b/api/proto/ghttpproto/http.pb.go index de462c5513a7..3413b129bd51 100644 --- a/api/proto/ghttpproto/http.pb.go +++ b/api/proto/ghttpproto/http.pb.go @@ -9,6 +9,7 @@ package ghttpproto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -20,89 +21,36 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Userinfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` - PasswordSet bool `protobuf:"varint,3,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"` -} - -func (x *Userinfo) Reset() { - *x = Userinfo{} - if protoimpl.UnsafeEnabled { - mi := &file_ghttpproto_http_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Userinfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Userinfo) ProtoMessage() {} - -func (x *Userinfo) ProtoReflect() protoreflect.Message { - mi := &file_ghttpproto_http_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Userinfo.ProtoReflect.Descriptor instead. -func (*Userinfo) Descriptor() ([]byte, []int) { - return file_ghttpproto_http_proto_rawDescGZIP(), []int{0} -} - -func (x *Userinfo) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -func (x *Userinfo) GetPassword() string { - if x != nil { - return x.Password - } - return "" -} - -func (x *Userinfo) GetPasswordSet() bool { - if x != nil { - return x.PasswordSet - } - return false -} - +// URL is a net.URL see: https://pkg.go.dev/net/url#URL type URL struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` - Opaque string `protobuf:"bytes,2,opt,name=opaque,proto3" json:"opaque,omitempty"` - User *Userinfo `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"` - Host string `protobuf:"bytes,4,opt,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` - RawPath string `protobuf:"bytes,6,opt,name=raw_path,json=rawPath,proto3" json:"raw_path,omitempty"` - ForceQuery bool `protobuf:"varint,7,opt,name=force_query,json=forceQuery,proto3" json:"force_query,omitempty"` - RawQuery string `protobuf:"bytes,8,opt,name=raw_query,json=rawQuery,proto3" json:"raw_query,omitempty"` - Fragment string `protobuf:"bytes,9,opt,name=fragment,proto3" json:"fragment,omitempty"` + // scheme is the url scheme name + Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"` + // opaque is encoded opaque data + Opaque string `protobuf:"bytes,2,opt,name=opaque,proto3" json:"opaque,omitempty"` + // user is username and password information + User *Userinfo `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"` + // host can be in the format host or host:port + Host string `protobuf:"bytes,4,opt,name=host,proto3" json:"host,omitempty"` + // path (relative paths may omit leading slash) + Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` + // raw_path is encoded path hint (see EscapedPath method) + RawPath string `protobuf:"bytes,6,opt,name=raw_path,json=rawPath,proto3" json:"raw_path,omitempty"` + // force is append a query ('?') even if RawQuery is empty + ForceQuery bool `protobuf:"varint,7,opt,name=force_query,json=forceQuery,proto3" json:"force_query,omitempty"` + // raw_query is encoded query values, without '?' + RawQuery string `protobuf:"bytes,8,opt,name=raw_query,json=rawQuery,proto3" json:"raw_query,omitempty"` + // fragment is fragment for references, without '#' + Fragment string `protobuf:"bytes,9,opt,name=fragment,proto3" json:"fragment,omitempty"` } func (x *URL) Reset() { *x = URL{} if protoimpl.UnsafeEnabled { - mi := &file_ghttpproto_http_proto_msgTypes[1] + mi := &file_ghttpproto_http_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -115,7 +63,7 @@ func (x *URL) String() string { func (*URL) ProtoMessage() {} func (x *URL) ProtoReflect() protoreflect.Message { - mi := &file_ghttpproto_http_proto_msgTypes[1] + mi := &file_ghttpproto_http_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -128,7 +76,7 @@ func (x *URL) ProtoReflect() protoreflect.Message { // Deprecated: Use URL.ProtoReflect.Descriptor instead. func (*URL) Descriptor() ([]byte, []int) { - return file_ghttpproto_http_proto_rawDescGZIP(), []int{1} + return file_ghttpproto_http_proto_rawDescGZIP(), []int{0} } func (x *URL) GetScheme() string { @@ -194,12 +142,81 @@ func (x *URL) GetFragment() string { return "" } +// UserInfo is net.Userinfo see: https://pkg.go.dev/net/url#Userinfo +type Userinfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // username is the username for the user + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // password is the password for the user + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + // password_set is a boolean which is true if the passord is set + PasswordSet bool `protobuf:"varint,3,opt,name=password_set,json=passwordSet,proto3" json:"password_set,omitempty"` +} + +func (x *Userinfo) Reset() { + *x = Userinfo{} + if protoimpl.UnsafeEnabled { + mi := &file_ghttpproto_http_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Userinfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Userinfo) ProtoMessage() {} + +func (x *Userinfo) ProtoReflect() protoreflect.Message { + mi := &file_ghttpproto_http_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Userinfo.ProtoReflect.Descriptor instead. +func (*Userinfo) Descriptor() ([]byte, []int) { + return file_ghttpproto_http_proto_rawDescGZIP(), []int{1} +} + +func (x *Userinfo) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Userinfo) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Userinfo) GetPasswordSet() bool { + if x != nil { + return x.PasswordSet + } + return false +} + type Element struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // key is a element key in a key value pair + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // values are a list of strings coresponding to the key Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` } @@ -254,6 +271,7 @@ type Certificates struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // cert is the certificate body Cert [][]byte `protobuf:"bytes,1,rep,name=cert,proto3" json:"cert,omitempty"` } @@ -296,23 +314,39 @@ func (x *Certificates) GetCert() [][]byte { return nil } +// ConnectionState is tls.ConnectionState see: https://pkg.go.dev/crypto/tls#ConnectionState type ConnectionState struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - HandshakeComplete bool `protobuf:"varint,2,opt,name=handshake_complete,json=handshakeComplete,proto3" json:"handshake_complete,omitempty"` - DidResume bool `protobuf:"varint,3,opt,name=did_resume,json=didResume,proto3" json:"did_resume,omitempty"` - CipherSuite uint32 `protobuf:"varint,4,opt,name=cipher_suite,json=cipherSuite,proto3" json:"cipher_suite,omitempty"` - NegotiatedProtocol string `protobuf:"bytes,5,opt,name=negotiated_protocol,json=negotiatedProtocol,proto3" json:"negotiated_protocol,omitempty"` - NegotiatedProtocolIsMutual bool `protobuf:"varint,6,opt,name=negotiated_protocol_is_mutual,json=negotiatedProtocolIsMutual,proto3" json:"negotiated_protocol_is_mutual,omitempty"` - ServerName string `protobuf:"bytes,7,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` - PeerCertificates *Certificates `protobuf:"bytes,8,opt,name=peer_certificates,json=peerCertificates,proto3" json:"peer_certificates,omitempty"` - VerifiedChains []*Certificates `protobuf:"bytes,9,rep,name=verified_chains,json=verifiedChains,proto3" json:"verified_chains,omitempty"` - SignedCertificateTimestamps [][]byte `protobuf:"bytes,10,rep,name=signed_certificate_timestamps,json=signedCertificateTimestamps,proto3" json:"signed_certificate_timestamps,omitempty"` - OcspResponse []byte `protobuf:"bytes,11,opt,name=ocsp_response,json=ocspResponse,proto3" json:"ocsp_response,omitempty"` - TlsUnique []byte `protobuf:"bytes,12,opt,name=tls_unique,json=tlsUnique,proto3" json:"tls_unique,omitempty"` + // version is the TLS version used by the connection (e.g. VersionTLS12) + Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // handshake_complete is true if the handshake has concluded + HandshakeComplete bool `protobuf:"varint,2,opt,name=handshake_complete,json=handshakeComplete,proto3" json:"handshake_complete,omitempty"` + // did_resume is true if this connection was successfully resumed from a + // previous session with a session ticket or similar mechanism + DidResume bool `protobuf:"varint,3,opt,name=did_resume,json=didResume,proto3" json:"did_resume,omitempty"` + // cipher_suite is the cipher suite negotiated for the connection + CipherSuite uint32 `protobuf:"varint,4,opt,name=cipher_suite,json=cipherSuite,proto3" json:"cipher_suite,omitempty"` + // negotiated_protocol is the application protocol negotiated with ALPN + NegotiatedProtocol string `protobuf:"bytes,5,opt,name=negotiated_protocol,json=negotiatedProtocol,proto3" json:"negotiated_protocol,omitempty"` + // server_name is the value of the Server Name Indication extension sent by + // the client + ServerName string `protobuf:"bytes,7,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` + // peer_certificates are the parsed certificates sent by the peer, in the + // order in which they were sent + PeerCertificates *Certificates `protobuf:"bytes,8,opt,name=peer_certificates,json=peerCertificates,proto3" json:"peer_certificates,omitempty"` + // verified_chains is a list of one or more chains where the first element is + // PeerCertificates[0] and the last element is from Config.RootCAs (on the + // client side) or Config.ClientCAs (on the server side). + VerifiedChains []*Certificates `protobuf:"bytes,9,rep,name=verified_chains,json=verifiedChains,proto3" json:"verified_chains,omitempty"` + // signed_certificate_timestamps is a list of SCTs provided by the peer + // through the TLS handshake for the leaf certificate, if any + SignedCertificateTimestamps [][]byte `protobuf:"bytes,10,rep,name=signed_certificate_timestamps,json=signedCertificateTimestamps,proto3" json:"signed_certificate_timestamps,omitempty"` + // ocsp_response is a stapled Online Certificate Status Protocol (OCSP) + // response provided by the peer for the leaf certificate, if any. + OcspResponse []byte `protobuf:"bytes,11,opt,name=ocsp_response,json=ocspResponse,proto3" json:"ocsp_response,omitempty"` } func (x *ConnectionState) Reset() { @@ -382,13 +416,6 @@ func (x *ConnectionState) GetNegotiatedProtocol() string { return "" } -func (x *ConnectionState) GetNegotiatedProtocolIsMutual() bool { - if x != nil { - return x.NegotiatedProtocolIsMutual - } - return false -} - func (x *ConnectionState) GetServerName() string { if x != nil { return x.ServerName @@ -424,34 +451,50 @@ func (x *ConnectionState) GetOcspResponse() []byte { return nil } -func (x *ConnectionState) GetTlsUnique() []byte { - if x != nil { - return x.TlsUnique - } - return nil -} - +// Request is an http.Request see: https://pkg.go.dev/net/http#Request type Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - Url *URL `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - Proto string `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` - ProtoMajor int32 `protobuf:"varint,4,opt,name=proto_major,json=protoMajor,proto3" json:"proto_major,omitempty"` - ProtoMinor int32 `protobuf:"varint,5,opt,name=proto_minor,json=protoMinor,proto3" json:"proto_minor,omitempty"` - Header []*Element `protobuf:"bytes,6,rep,name=header,proto3" json:"header,omitempty"` - Body uint32 `protobuf:"varint,7,opt,name=body,proto3" json:"body,omitempty"` // server ID - ContentLength int64 `protobuf:"varint,8,opt,name=content_length,json=contentLength,proto3" json:"content_length,omitempty"` - TransferEncoding []string `protobuf:"bytes,9,rep,name=transfer_encoding,json=transferEncoding,proto3" json:"transfer_encoding,omitempty"` - Host string `protobuf:"bytes,10,opt,name=host,proto3" json:"host,omitempty"` - Form []*Element `protobuf:"bytes,11,rep,name=form,proto3" json:"form,omitempty"` - PostForm []*Element `protobuf:"bytes,12,rep,name=post_form,json=postForm,proto3" json:"post_form,omitempty"` - TrailerKeys []string `protobuf:"bytes,13,rep,name=trailer_keys,json=trailerKeys,proto3" json:"trailer_keys,omitempty"` - RemoteAddr string `protobuf:"bytes,14,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"` - RequestUri string `protobuf:"bytes,15,opt,name=request_uri,json=requestUri,proto3" json:"request_uri,omitempty"` - Tls *ConnectionState `protobuf:"bytes,16,opt,name=tls,proto3" json:"tls,omitempty"` + // method specifies the HTTP method (GET, POST, PUT, etc.) + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + // url specifies either the URI being requested (for server requests) + // or the URL to access (for client requests) + Url *URL `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // proto is the protocol version for incoming server requests + Proto string `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` + // proto_major is the major version + ProtoMajor int32 `protobuf:"varint,4,opt,name=proto_major,json=protoMajor,proto3" json:"proto_major,omitempty"` + // proto_minor is the minor version + ProtoMinor int32 `protobuf:"varint,5,opt,name=proto_minor,json=protoMinor,proto3" json:"proto_minor,omitempty"` + // header contains the request header fields either received + // by the server or to be sent by the client + Header []*Element `protobuf:"bytes,6,rep,name=header,proto3" json:"header,omitempty"` + // body is the request payload in bytes + Body []byte `protobuf:"bytes,7,opt,name=body,proto3" json:"body,omitempty"` + // content_length records the length of the associated content + ContentLength int64 `protobuf:"varint,8,opt,name=content_length,json=contentLength,proto3" json:"content_length,omitempty"` + // transfer_encoding lists the transfer encodings from outermost to + // innermost + TransferEncoding []string `protobuf:"bytes,9,rep,name=transfer_encoding,json=transferEncoding,proto3" json:"transfer_encoding,omitempty"` + // host specifies the host on which the URL is sought + Host string `protobuf:"bytes,10,opt,name=host,proto3" json:"host,omitempty"` + // form contains the parsed form data, including both the URL + // field's query parameters and the PATCH, POST, or PUT form data + Form []*Element `protobuf:"bytes,11,rep,name=form,proto3" json:"form,omitempty"` + // post_form contains the parsed form data from PATCH, POST + // or PUT body parameters + PostForm []*Element `protobuf:"bytes,12,rep,name=post_form,json=postForm,proto3" json:"post_form,omitempty"` + // trailer_keys specifies additional headers that are sent after the request + TrailerKeys []string `protobuf:"bytes,13,rep,name=trailer_keys,json=trailerKeys,proto3" json:"trailer_keys,omitempty"` + // remote_addr allows HTTP servers and other software to record + // the network address that sent the request + RemoteAddr string `protobuf:"bytes,14,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"` + // request_uri is the unmodified request-target + RequestUri string `protobuf:"bytes,15,opt,name=request_uri,json=requestUri,proto3" json:"request_uri,omitempty"` + // tls connection state + Tls *ConnectionState `protobuf:"bytes,16,opt,name=tls,proto3" json:"tls,omitempty"` } func (x *Request) Reset() { @@ -528,11 +571,11 @@ func (x *Request) GetHeader() []*Element { return nil } -func (x *Request) GetBody() uint32 { +func (x *Request) GetBody() []byte { if x != nil { return x.Body } - return 0 + return nil } func (x *Request) GetContentLength() int64 { @@ -603,7 +646,10 @@ type ResponseWriter struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // server ID + // id correlates to a stream id of the gRPC server hosting the Writer service + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // header returns the header map that will be sent by + // WriteHeader. Header []*Element `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty"` } @@ -658,8 +704,10 @@ type HTTPRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // response_writer is used by an HTTP handler to construct an HTTP response ResponseWriter *ResponseWriter `protobuf:"bytes,1,opt,name=response_writer,json=responseWriter,proto3" json:"response_writer,omitempty"` - Request *Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + // request is an http request + Request *Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } func (x *HTTPRequest) Reset() { @@ -708,14 +756,24 @@ func (x *HTTPRequest) GetRequest() *Request { return nil } -type HTTPResponse struct { +type HandleSimpleHTTPRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // method specifies the HTTP method (GET, POST, PUT, etc.) + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + // url specifies either the URI being requested + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + // headers contains the request header fields either received + // by the server or to be sent by the client + Headers []*Element `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty"` + // body is the request payload in bytes + Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` } -func (x *HTTPResponse) Reset() { - *x = HTTPResponse{} +func (x *HandleSimpleHTTPRequest) Reset() { + *x = HandleSimpleHTTPRequest{} if protoimpl.UnsafeEnabled { mi := &file_ghttpproto_http_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -723,13 +781,13 @@ func (x *HTTPResponse) Reset() { } } -func (x *HTTPResponse) String() string { +func (x *HandleSimpleHTTPRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*HTTPResponse) ProtoMessage() {} +func (*HandleSimpleHTTPRequest) ProtoMessage() {} -func (x *HTTPResponse) ProtoReflect() protoreflect.Message { +func (x *HandleSimpleHTTPRequest) ProtoReflect() protoreflect.Message { mi := &file_ghttpproto_http_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -741,139 +799,250 @@ func (x *HTTPResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use HTTPResponse.ProtoReflect.Descriptor instead. -func (*HTTPResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use HandleSimpleHTTPRequest.ProtoReflect.Descriptor instead. +func (*HandleSimpleHTTPRequest) Descriptor() ([]byte, []int) { return file_ghttpproto_http_proto_rawDescGZIP(), []int{8} } +func (x *HandleSimpleHTTPRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *HandleSimpleHTTPRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *HandleSimpleHTTPRequest) GetHeaders() []*Element { + if x != nil { + return x.Headers + } + return nil +} + +func (x *HandleSimpleHTTPRequest) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +type HandleSimpleHTTPResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // code is the response code + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + // headers contains the request header fields either received + // by the server or to be sent by the client + Headers []*Element `protobuf:"bytes,2,rep,name=headers,proto3" json:"headers,omitempty"` + // body is the response payload in bytes + Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *HandleSimpleHTTPResponse) Reset() { + *x = HandleSimpleHTTPResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ghttpproto_http_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HandleSimpleHTTPResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HandleSimpleHTTPResponse) ProtoMessage() {} + +func (x *HandleSimpleHTTPResponse) ProtoReflect() protoreflect.Message { + mi := &file_ghttpproto_http_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HandleSimpleHTTPResponse.ProtoReflect.Descriptor instead. +func (*HandleSimpleHTTPResponse) Descriptor() ([]byte, []int) { + return file_ghttpproto_http_proto_rawDescGZIP(), []int{9} +} + +func (x *HandleSimpleHTTPResponse) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *HandleSimpleHTTPResponse) GetHeaders() []*Element { + if x != nil { + return x.Headers + } + return nil +} + +func (x *HandleSimpleHTTPResponse) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + var File_ghttpproto_http_proto protoreflect.FileDescriptor var file_ghttpproto_http_proto_rawDesc = []byte{ 0x0a, 0x15, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x74, 0x22, 0xfc, 0x01, 0x0a, 0x03, 0x55, - 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x70, - 0x61, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x70, 0x61, 0x71, - 0x75, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, - 0x68, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x61, 0x77, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x61, 0x77, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x61, 0x77, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, - 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x33, 0x0a, 0x07, 0x45, 0x6c, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x22, - 0x0a, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, - 0x72, 0x74, 0x22, 0xc3, 0x04, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2d, 0x0a, 0x12, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, - 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x64, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x69, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, - 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, - 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x12, 0x41, 0x0a, 0x1d, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x73, 0x5f, 0x6d, 0x75, 0x74, - 0x75, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6e, 0x65, 0x67, 0x6f, 0x74, - 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x73, 0x4d, - 0x75, 0x74, 0x75, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x10, 0x70, 0x65, 0x65, - 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, - 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6c, 0x73, - 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, - 0x6c, 0x73, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x22, 0xb4, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x03, - 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x68, 0x74, 0x74, - 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x52, 0x4c, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, - 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, - 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, - 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x66, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, - 0x68, 0x6f, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x12, 0x27, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, + 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0xfc, 0x01, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x61, + 0x77, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x61, + 0x77, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x61, 0x77, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, + 0x65, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, + 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x53, 0x65, 0x74, 0x22, 0x33, 0x0a, 0x07, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, + 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x22, + 0xed, 0x03, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, + 0x12, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x6e, 0x64, 0x73, + 0x68, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x64, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x64, 0x69, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x2f, + 0x0a, 0x13, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x45, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x68, + 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x10, 0x70, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x1b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x22, + 0xb4, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x21, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x52, + 0x4c, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x2b, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x30, 0x0a, 0x09, 0x70, 0x6f, 0x73, - 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, - 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, - 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1f, - 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x72, 0x69, - 0x12, 0x2d, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x22, - 0x4d, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x81, - 0x01, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, - 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0x43, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x3b, 0x0a, 0x06, 0x48, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x17, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, - 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, - 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, + 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, + 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6d, + 0x12, 0x30, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0c, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x46, 0x6f, + 0x72, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x6b, 0x65, + 0x79, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, + 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x55, 0x72, 0x69, 0x12, 0x2d, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x22, 0x4d, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, + 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x17, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, + 0x2d, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x22, 0x71, 0x0a, 0x18, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x32, 0x9c, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x39, + 0x0a, 0x06, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x17, 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x59, 0x0a, 0x0c, 0x48, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x23, 0x2e, 0x67, 0x68, 0x74, 0x74, + 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x53, 0x69, 0x6d, + 0x70, 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x68, 0x74, 0x74, + 0x70, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -888,23 +1057,25 @@ func file_ghttpproto_http_proto_rawDescGZIP() []byte { return file_ghttpproto_http_proto_rawDescData } -var file_ghttpproto_http_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_ghttpproto_http_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_ghttpproto_http_proto_goTypes = []interface{}{ - (*Userinfo)(nil), // 0: ghttpproto.Userinfo - (*URL)(nil), // 1: ghttpproto.URL - (*Element)(nil), // 2: ghttpproto.Element - (*Certificates)(nil), // 3: ghttpproto.Certificates - (*ConnectionState)(nil), // 4: ghttpproto.ConnectionState - (*Request)(nil), // 5: ghttpproto.Request - (*ResponseWriter)(nil), // 6: ghttpproto.ResponseWriter - (*HTTPRequest)(nil), // 7: ghttpproto.HTTPRequest - (*HTTPResponse)(nil), // 8: ghttpproto.HTTPResponse + (*URL)(nil), // 0: ghttpproto.URL + (*Userinfo)(nil), // 1: ghttpproto.Userinfo + (*Element)(nil), // 2: ghttpproto.Element + (*Certificates)(nil), // 3: ghttpproto.Certificates + (*ConnectionState)(nil), // 4: ghttpproto.ConnectionState + (*Request)(nil), // 5: ghttpproto.Request + (*ResponseWriter)(nil), // 6: ghttpproto.ResponseWriter + (*HTTPRequest)(nil), // 7: ghttpproto.HTTPRequest + (*HandleSimpleHTTPRequest)(nil), // 8: ghttpproto.HandleSimpleHTTPRequest + (*HandleSimpleHTTPResponse)(nil), // 9: ghttpproto.HandleSimpleHTTPResponse + (*emptypb.Empty)(nil), // 10: google.protobuf.Empty } var file_ghttpproto_http_proto_depIdxs = []int32{ - 0, // 0: ghttpproto.URL.user:type_name -> ghttpproto.Userinfo + 1, // 0: ghttpproto.URL.user:type_name -> ghttpproto.Userinfo 3, // 1: ghttpproto.ConnectionState.peer_certificates:type_name -> ghttpproto.Certificates 3, // 2: ghttpproto.ConnectionState.verified_chains:type_name -> ghttpproto.Certificates - 1, // 3: ghttpproto.Request.url:type_name -> ghttpproto.URL + 0, // 3: ghttpproto.Request.url:type_name -> ghttpproto.URL 2, // 4: ghttpproto.Request.header:type_name -> ghttpproto.Element 2, // 5: ghttpproto.Request.form:type_name -> ghttpproto.Element 2, // 6: ghttpproto.Request.post_form:type_name -> ghttpproto.Element @@ -912,13 +1083,17 @@ var file_ghttpproto_http_proto_depIdxs = []int32{ 2, // 8: ghttpproto.ResponseWriter.header:type_name -> ghttpproto.Element 6, // 9: ghttpproto.HTTPRequest.response_writer:type_name -> ghttpproto.ResponseWriter 5, // 10: ghttpproto.HTTPRequest.request:type_name -> ghttpproto.Request - 7, // 11: ghttpproto.HTTP.Handle:input_type -> ghttpproto.HTTPRequest - 8, // 12: ghttpproto.HTTP.Handle:output_type -> ghttpproto.HTTPResponse - 12, // [12:13] is the sub-list for method output_type - 11, // [11:12] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 2, // 11: ghttpproto.HandleSimpleHTTPRequest.headers:type_name -> ghttpproto.Element + 2, // 12: ghttpproto.HandleSimpleHTTPResponse.headers:type_name -> ghttpproto.Element + 7, // 13: ghttpproto.HTTP.Handle:input_type -> ghttpproto.HTTPRequest + 8, // 14: ghttpproto.HTTP.HandleSimple:input_type -> ghttpproto.HandleSimpleHTTPRequest + 10, // 15: ghttpproto.HTTP.Handle:output_type -> google.protobuf.Empty + 9, // 16: ghttpproto.HTTP.HandleSimple:output_type -> ghttpproto.HandleSimpleHTTPResponse + 15, // [15:17] is the sub-list for method output_type + 13, // [13:15] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_ghttpproto_http_proto_init() } @@ -928,7 +1103,7 @@ func file_ghttpproto_http_proto_init() { } if !protoimpl.UnsafeEnabled { file_ghttpproto_http_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Userinfo); i { + switch v := v.(*URL); i { case 0: return &v.state case 1: @@ -940,7 +1115,7 @@ func file_ghttpproto_http_proto_init() { } } file_ghttpproto_http_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*URL); i { + switch v := v.(*Userinfo); i { case 0: return &v.state case 1: @@ -1024,7 +1199,19 @@ func file_ghttpproto_http_proto_init() { } } file_ghttpproto_http_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPResponse); i { + switch v := v.(*HandleSimpleHTTPRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ghttpproto_http_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HandleSimpleHTTPResponse); i { case 0: return &v.state case 1: @@ -1042,7 +1229,7 @@ func file_ghttpproto_http_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ghttpproto_http_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 10, NumExtensions: 0, NumServices: 1, }, diff --git a/api/proto/ghttpproto/http_grpc.pb.go b/api/proto/ghttpproto/http_grpc.pb.go index 621184c5f035..3d3a91f68abb 100644 --- a/api/proto/ghttpproto/http_grpc.pb.go +++ b/api/proto/ghttpproto/http_grpc.pb.go @@ -11,6 +11,7 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -22,7 +23,14 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type HTTPClient interface { - Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error) + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + HandleSimple(ctx context.Context, in *HandleSimpleHTTPRequest, opts ...grpc.CallOption) (*HandleSimpleHTTPResponse, error) } type hTTPClient struct { @@ -33,8 +41,8 @@ func NewHTTPClient(cc grpc.ClientConnInterface) HTTPClient { return &hTTPClient{cc} } -func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error) { - out := new(HTTPResponse) +func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/ghttpproto.HTTP/Handle", in, out, opts...) if err != nil { return nil, err @@ -42,11 +50,27 @@ func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.C return out, nil } +func (c *hTTPClient) HandleSimple(ctx context.Context, in *HandleSimpleHTTPRequest, opts ...grpc.CallOption) (*HandleSimpleHTTPResponse, error) { + out := new(HandleSimpleHTTPResponse) + err := c.cc.Invoke(ctx, "/ghttpproto.HTTP/HandleSimple", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // HTTPServer is the server API for HTTP service. // All implementations must embed UnimplementedHTTPServer // for forward compatibility type HTTPServer interface { - Handle(context.Context, *HTTPRequest) (*HTTPResponse, error) + // Handle wraps http1 over http2 and provides support for websockets by implementing + // net conn and responsewriter in http2. + Handle(context.Context, *HTTPRequest) (*emptypb.Empty, error) + // HandleSimple wraps http1 requests over http2 similar to Handle but only passes headers + // and body bytes. Because the request and response are single protos with no inline + // gRPC servers the CPU cost as well as file descriptor overhead is less + // (no additional goroutines). + HandleSimple(context.Context, *HandleSimpleHTTPRequest) (*HandleSimpleHTTPResponse, error) mustEmbedUnimplementedHTTPServer() } @@ -54,9 +78,12 @@ type HTTPServer interface { type UnimplementedHTTPServer struct { } -func (UnimplementedHTTPServer) Handle(context.Context, *HTTPRequest) (*HTTPResponse, error) { +func (UnimplementedHTTPServer) Handle(context.Context, *HTTPRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Handle not implemented") } +func (UnimplementedHTTPServer) HandleSimple(context.Context, *HandleSimpleHTTPRequest) (*HandleSimpleHTTPResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HandleSimple not implemented") +} func (UnimplementedHTTPServer) mustEmbedUnimplementedHTTPServer() {} // UnsafeHTTPServer may be embedded to opt out of forward compatibility for this service. @@ -88,6 +115,24 @@ func _HTTP_Handle_Handler(srv interface{}, ctx context.Context, dec func(interfa return interceptor(ctx, in, info, handler) } +func _HTTP_HandleSimple_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HandleSimpleHTTPRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HTTPServer).HandleSimple(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ghttpproto.HTTP/HandleSimple", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HTTPServer).HandleSimple(ctx, req.(*HandleSimpleHTTPRequest)) + } + return interceptor(ctx, in, info, handler) +} + // HTTP_ServiceDesc is the grpc.ServiceDesc for HTTP service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -99,6 +144,10 @@ var HTTP_ServiceDesc = grpc.ServiceDesc{ MethodName: "Handle", Handler: _HTTP_Handle_Handler, }, + { + MethodName: "HandleSimple", + Handler: _HTTP_HandleSimple_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ghttpproto/http.proto", diff --git a/api/proto/greadcloserproto/readcloser.pb.go b/api/proto/greadcloserproto/readcloser.pb.go deleted file mode 100644 index f6e36df92bd4..000000000000 --- a/api/proto/greadcloserproto/readcloser.pb.go +++ /dev/null @@ -1,345 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc (unknown) -// source: greadcloserproto/readcloser.proto - -package greadcloserproto - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type ReadRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` -} - -func (x *ReadRequest) Reset() { - *x = ReadRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadRequest) ProtoMessage() {} - -func (x *ReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. -func (*ReadRequest) Descriptor() ([]byte, []int) { - return file_greadcloserproto_readcloser_proto_rawDescGZIP(), []int{0} -} - -func (x *ReadRequest) GetLength() int32 { - if x != nil { - return x.Length - } - return 0 -} - -type ReadResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` -} - -func (x *ReadResponse) Reset() { - *x = ReadResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadResponse) ProtoMessage() {} - -func (x *ReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. -func (*ReadResponse) Descriptor() ([]byte, []int) { - return file_greadcloserproto_readcloser_proto_rawDescGZIP(), []int{1} -} - -func (x *ReadResponse) GetRead() []byte { - if x != nil { - return x.Read - } - return nil -} - -func (x *ReadResponse) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -func (x *ReadResponse) GetErrored() bool { - if x != nil { - return x.Errored - } - return false -} - -type CloseRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CloseRequest) Reset() { - *x = CloseRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseRequest) ProtoMessage() {} - -func (x *CloseRequest) ProtoReflect() protoreflect.Message { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseRequest.ProtoReflect.Descriptor instead. -func (*CloseRequest) Descriptor() ([]byte, []int) { - return file_greadcloserproto_readcloser_proto_rawDescGZIP(), []int{2} -} - -type CloseResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CloseResponse) Reset() { - *x = CloseResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseResponse) ProtoMessage() {} - -func (x *CloseResponse) ProtoReflect() protoreflect.Message { - mi := &file_greadcloserproto_readcloser_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseResponse.ProtoReflect.Descriptor instead. -func (*CloseResponse) Descriptor() ([]byte, []int) { - return file_greadcloserproto_readcloser_proto_rawDescGZIP(), []int{3} -} - -var File_greadcloserproto_readcloser_proto protoreflect.FileDescriptor - -var file_greadcloserproto_readcloser_proto_rawDesc = []byte{ - 0x0a, 0x21, 0x67, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x67, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x52, 0x0a, 0x0c, - 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x72, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x72, 0x65, 0x61, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, - 0x22, 0x0e, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0x99, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x04, - 0x52, 0x65, 0x61, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, - 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, - 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x1e, 0x2e, 0x67, - 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x36, 0x5a, - 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, - 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x65, 0x61, 0x64, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_greadcloserproto_readcloser_proto_rawDescOnce sync.Once - file_greadcloserproto_readcloser_proto_rawDescData = file_greadcloserproto_readcloser_proto_rawDesc -) - -func file_greadcloserproto_readcloser_proto_rawDescGZIP() []byte { - file_greadcloserproto_readcloser_proto_rawDescOnce.Do(func() { - file_greadcloserproto_readcloser_proto_rawDescData = protoimpl.X.CompressGZIP(file_greadcloserproto_readcloser_proto_rawDescData) - }) - return file_greadcloserproto_readcloser_proto_rawDescData -} - -var file_greadcloserproto_readcloser_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_greadcloserproto_readcloser_proto_goTypes = []interface{}{ - (*ReadRequest)(nil), // 0: greadcloserproto.ReadRequest - (*ReadResponse)(nil), // 1: greadcloserproto.ReadResponse - (*CloseRequest)(nil), // 2: greadcloserproto.CloseRequest - (*CloseResponse)(nil), // 3: greadcloserproto.CloseResponse -} -var file_greadcloserproto_readcloser_proto_depIdxs = []int32{ - 0, // 0: greadcloserproto.Reader.Read:input_type -> greadcloserproto.ReadRequest - 2, // 1: greadcloserproto.Reader.Close:input_type -> greadcloserproto.CloseRequest - 1, // 2: greadcloserproto.Reader.Read:output_type -> greadcloserproto.ReadResponse - 3, // 3: greadcloserproto.Reader.Close:output_type -> greadcloserproto.CloseResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_greadcloserproto_readcloser_proto_init() } -func file_greadcloserproto_readcloser_proto_init() { - if File_greadcloserproto_readcloser_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_greadcloserproto_readcloser_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_greadcloserproto_readcloser_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_greadcloserproto_readcloser_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_greadcloserproto_readcloser_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_greadcloserproto_readcloser_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_greadcloserproto_readcloser_proto_goTypes, - DependencyIndexes: file_greadcloserproto_readcloser_proto_depIdxs, - MessageInfos: file_greadcloserproto_readcloser_proto_msgTypes, - }.Build() - File_greadcloserproto_readcloser_proto = out.File - file_greadcloserproto_readcloser_proto_rawDesc = nil - file_greadcloserproto_readcloser_proto_goTypes = nil - file_greadcloserproto_readcloser_proto_depIdxs = nil -} diff --git a/api/proto/greadcloserproto/readcloser_grpc.pb.go b/api/proto/greadcloserproto/readcloser_grpc.pb.go deleted file mode 100644 index cbe98dfcc881..000000000000 --- a/api/proto/greadcloserproto/readcloser_grpc.pb.go +++ /dev/null @@ -1,141 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc (unknown) -// source: greadcloserproto/readcloser.proto - -package greadcloserproto - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// ReaderClient is the client API for Reader service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type ReaderClient interface { - Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) - Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) -} - -type readerClient struct { - cc grpc.ClientConnInterface -} - -func NewReaderClient(cc grpc.ClientConnInterface) ReaderClient { - return &readerClient{cc} -} - -func (c *readerClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) { - out := new(ReadResponse) - err := c.cc.Invoke(ctx, "/greadcloserproto.Reader/Read", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *readerClient) Close(ctx context.Context, in *CloseRequest, opts ...grpc.CallOption) (*CloseResponse, error) { - out := new(CloseResponse) - err := c.cc.Invoke(ctx, "/greadcloserproto.Reader/Close", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ReaderServer is the server API for Reader service. -// All implementations must embed UnimplementedReaderServer -// for forward compatibility -type ReaderServer interface { - Read(context.Context, *ReadRequest) (*ReadResponse, error) - Close(context.Context, *CloseRequest) (*CloseResponse, error) - mustEmbedUnimplementedReaderServer() -} - -// UnimplementedReaderServer must be embedded to have forward compatible implementations. -type UnimplementedReaderServer struct { -} - -func (UnimplementedReaderServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") -} -func (UnimplementedReaderServer) Close(context.Context, *CloseRequest) (*CloseResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") -} -func (UnimplementedReaderServer) mustEmbedUnimplementedReaderServer() {} - -// UnsafeReaderServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ReaderServer will -// result in compilation errors. -type UnsafeReaderServer interface { - mustEmbedUnimplementedReaderServer() -} - -func RegisterReaderServer(s grpc.ServiceRegistrar, srv ReaderServer) { - s.RegisterService(&Reader_ServiceDesc, srv) -} - -func _Reader_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReadRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ReaderServer).Read(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/greadcloserproto.Reader/Read", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ReaderServer).Read(ctx, req.(*ReadRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Reader_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CloseRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ReaderServer).Close(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/greadcloserproto.Reader/Close", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ReaderServer).Close(ctx, req.(*CloseRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// Reader_ServiceDesc is the grpc.ServiceDesc for Reader service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Reader_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "greadcloserproto.Reader", - HandlerType: (*ReaderServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Read", - Handler: _Reader_Read_Handler, - }, - { - MethodName: "Close", - Handler: _Reader_Close_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "greadcloserproto/readcloser.proto", -} diff --git a/api/proto/greaderproto/reader.pb.go b/api/proto/greaderproto/reader.pb.go index f1b3bce691a1..d8bd580b7df8 100644 --- a/api/proto/greaderproto/reader.pb.go +++ b/api/proto/greaderproto/reader.pb.go @@ -25,6 +25,7 @@ type ReadRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // length is the request in bytes Length int32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` } @@ -72,9 +73,12 @@ type ReadResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` + // read is the payload in bytes + Read []byte `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` + // error is an error message + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // errored is true if an error has been set + Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` } func (x *ReadResponse) Reset() { diff --git a/api/proto/gresponsewriterproto/responsewriter.pb.go b/api/proto/gresponsewriterproto/responsewriter.pb.go index 32469a2c1fa7..09d8cbbae8cf 100644 --- a/api/proto/gresponsewriterproto/responsewriter.pb.go +++ b/api/proto/gresponsewriterproto/responsewriter.pb.go @@ -9,6 +9,7 @@ package gresponsewriterproto import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -25,7 +26,9 @@ type Header struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // key is a element key in a key value pair + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // values are a list of strings coresponding to the key Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` } @@ -80,8 +83,10 @@ type WriteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // headers represents the key-value pairs in an HTTP header Headers []*Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + // payload is the write request in bytes + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *WriteRequest) Reset() { @@ -135,6 +140,7 @@ type WriteResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // written is the number of bytes written in body Written int32 `protobuf:"varint,1,opt,name=written,proto3" json:"written,omitempty"` } @@ -182,8 +188,10 @@ type WriteHeaderRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Headers []*Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` - StatusCode int32 `protobuf:"varint,2,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"` + // headers represents the key-value pairs in an HTTP header + Headers []*Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` + // status_code must be a valid HTTP 1xx-5xx status code + StatusCode int32 `protobuf:"varint,2,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"` } func (x *WriteHeaderRequest) Reset() { @@ -232,176 +240,28 @@ func (x *WriteHeaderRequest) GetStatusCode() int32 { return 0 } -type WriteHeaderResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *WriteHeaderResponse) Reset() { - *x = WriteHeaderResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WriteHeaderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WriteHeaderResponse) ProtoMessage() {} - -func (x *WriteHeaderResponse) ProtoReflect() protoreflect.Message { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WriteHeaderResponse.ProtoReflect.Descriptor instead. -func (*WriteHeaderResponse) Descriptor() ([]byte, []int) { - return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{4} -} - -type FlushRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *FlushRequest) Reset() { - *x = FlushRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FlushRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FlushRequest) ProtoMessage() {} - -func (x *FlushRequest) ProtoReflect() protoreflect.Message { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FlushRequest.ProtoReflect.Descriptor instead. -func (*FlushRequest) Descriptor() ([]byte, []int) { - return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{5} -} - -type FlushResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *FlushResponse) Reset() { - *x = FlushResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FlushResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FlushResponse) ProtoMessage() {} - -func (x *FlushResponse) ProtoReflect() protoreflect.Message { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FlushResponse.ProtoReflect.Descriptor instead. -func (*FlushResponse) Descriptor() ([]byte, []int) { - return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{6} -} - -type HijackRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *HijackRequest) Reset() { - *x = HijackRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HijackRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HijackRequest) ProtoMessage() {} - -func (x *HijackRequest) ProtoReflect() protoreflect.Message { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HijackRequest.ProtoReflect.Descriptor instead. -func (*HijackRequest) Descriptor() ([]byte, []int) { - return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{7} -} - type HijackResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConnServer uint32 `protobuf:"varint,1,opt,name=conn_server,json=connServer,proto3" json:"conn_server,omitempty"` - LocalNetwork string `protobuf:"bytes,2,opt,name=local_network,json=localNetwork,proto3" json:"local_network,omitempty"` - LocalString string `protobuf:"bytes,3,opt,name=local_string,json=localString,proto3" json:"local_string,omitempty"` + LocalNetwork string `protobuf:"bytes,2,opt,name=local_network,json=localNetwork,proto3" json:"local_network,omitempty"` + // local_string is string form of address + LocalString string `protobuf:"bytes,3,opt,name=local_string,json=localString,proto3" json:"local_string,omitempty"` + // remote_network is the name of the network (for example, "tcp", "udp") RemoteNetwork string `protobuf:"bytes,4,opt,name=remote_network,json=remoteNetwork,proto3" json:"remote_network,omitempty"` - RemoteString string `protobuf:"bytes,5,opt,name=remote_string,json=remoteString,proto3" json:"remote_string,omitempty"` - ReaderServer uint32 `protobuf:"varint,6,opt,name=reader_server,json=readerServer,proto3" json:"reader_server,omitempty"` - WriterServer uint32 `protobuf:"varint,7,opt,name=writer_server,json=writerServer,proto3" json:"writer_server,omitempty"` + // remote_string is string form of address + RemoteString string `protobuf:"bytes,5,opt,name=remote_string,json=remoteString,proto3" json:"remote_string,omitempty"` + // conn_read_writer_server is the stream id of the gRPC server + // serving the Conn, Reader and Writer services which facilitate + // Hijacking, + ConnReadWriterServer uint32 `protobuf:"varint,8,opt,name=conn_read_writer_server,json=connReadWriterServer,proto3" json:"conn_read_writer_server,omitempty"` } func (x *HijackResponse) Reset() { *x = HijackResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[8] + mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -414,7 +274,7 @@ func (x *HijackResponse) String() string { func (*HijackResponse) ProtoMessage() {} func (x *HijackResponse) ProtoReflect() protoreflect.Message { - mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[8] + mi := &file_gresponsewriterproto_responsewriter_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -427,14 +287,7 @@ func (x *HijackResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HijackResponse.ProtoReflect.Descriptor instead. func (*HijackResponse) Descriptor() ([]byte, []int) { - return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{8} -} - -func (x *HijackResponse) GetConnServer() uint32 { - if x != nil { - return x.ConnServer - } - return 0 + return file_gresponsewriterproto_responsewriter_proto_rawDescGZIP(), []int{4} } func (x *HijackResponse) GetLocalNetwork() string { @@ -465,16 +318,9 @@ func (x *HijackResponse) GetRemoteString() string { return "" } -func (x *HijackResponse) GetReaderServer() uint32 { - if x != nil { - return x.ReaderServer - } - return 0 -} - -func (x *HijackResponse) GetWriterServer() uint32 { +func (x *HijackResponse) GetConnReadWriterServer() uint32 { if x != nil { - return x.WriterServer + return x.ConnReadWriterServer } return 0 } @@ -486,74 +332,66 @@ var file_gresponsewriterproto_responsewriter_proto_rawDesc = []byte{ 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, - 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x32, + 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x22, 0x60, 0x0a, 0x0c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x36, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x22, + 0x6d, 0x0a, 0x12, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x72, 0x69, 0x74, - 0x74, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x77, 0x72, 0x69, 0x74, 0x74, - 0x65, 0x6e, 0x22, 0x6d, 0x0a, 0x12, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, - 0x65, 0x22, 0x15, 0x0a, 0x13, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x46, 0x6c, 0x75, 0x73, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x46, 0x6c, 0x75, 0x73, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x48, 0x69, 0x6a, - 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x02, 0x0a, 0x0e, 0x48, - 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x23, - 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x23, 0x0a, - 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, - 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x32, 0xe5, 0x02, 0x0a, - 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x05, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x12, 0x22, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, - 0x05, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x6c, - 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x22, 0xed, + 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x6e, 0x52, 0x65, 0x61, 0x64, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x01, + 0x10, 0x02, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x32, 0xac, + 0x02, 0x0a, 0x06, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x05, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0b, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x53, 0x0a, 0x06, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, 0x23, 0x2e, 0x67, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x05, + 0x46, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x06, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x24, 0x2e, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, + 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3a, 0x5a, + 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -568,29 +406,26 @@ func file_gresponsewriterproto_responsewriter_proto_rawDescGZIP() []byte { return file_gresponsewriterproto_responsewriter_proto_rawDescData } -var file_gresponsewriterproto_responsewriter_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_gresponsewriterproto_responsewriter_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_gresponsewriterproto_responsewriter_proto_goTypes = []interface{}{ - (*Header)(nil), // 0: gresponsewriterproto.Header - (*WriteRequest)(nil), // 1: gresponsewriterproto.WriteRequest - (*WriteResponse)(nil), // 2: gresponsewriterproto.WriteResponse - (*WriteHeaderRequest)(nil), // 3: gresponsewriterproto.WriteHeaderRequest - (*WriteHeaderResponse)(nil), // 4: gresponsewriterproto.WriteHeaderResponse - (*FlushRequest)(nil), // 5: gresponsewriterproto.FlushRequest - (*FlushResponse)(nil), // 6: gresponsewriterproto.FlushResponse - (*HijackRequest)(nil), // 7: gresponsewriterproto.HijackRequest - (*HijackResponse)(nil), // 8: gresponsewriterproto.HijackResponse + (*Header)(nil), // 0: gresponsewriterproto.Header + (*WriteRequest)(nil), // 1: gresponsewriterproto.WriteRequest + (*WriteResponse)(nil), // 2: gresponsewriterproto.WriteResponse + (*WriteHeaderRequest)(nil), // 3: gresponsewriterproto.WriteHeaderRequest + (*HijackResponse)(nil), // 4: gresponsewriterproto.HijackResponse + (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_gresponsewriterproto_responsewriter_proto_depIdxs = []int32{ 0, // 0: gresponsewriterproto.WriteRequest.headers:type_name -> gresponsewriterproto.Header 0, // 1: gresponsewriterproto.WriteHeaderRequest.headers:type_name -> gresponsewriterproto.Header 1, // 2: gresponsewriterproto.Writer.Write:input_type -> gresponsewriterproto.WriteRequest 3, // 3: gresponsewriterproto.Writer.WriteHeader:input_type -> gresponsewriterproto.WriteHeaderRequest - 5, // 4: gresponsewriterproto.Writer.Flush:input_type -> gresponsewriterproto.FlushRequest - 7, // 5: gresponsewriterproto.Writer.Hijack:input_type -> gresponsewriterproto.HijackRequest + 5, // 4: gresponsewriterproto.Writer.Flush:input_type -> google.protobuf.Empty + 5, // 5: gresponsewriterproto.Writer.Hijack:input_type -> google.protobuf.Empty 2, // 6: gresponsewriterproto.Writer.Write:output_type -> gresponsewriterproto.WriteResponse - 4, // 7: gresponsewriterproto.Writer.WriteHeader:output_type -> gresponsewriterproto.WriteHeaderResponse - 6, // 8: gresponsewriterproto.Writer.Flush:output_type -> gresponsewriterproto.FlushResponse - 8, // 9: gresponsewriterproto.Writer.Hijack:output_type -> gresponsewriterproto.HijackResponse + 5, // 7: gresponsewriterproto.Writer.WriteHeader:output_type -> google.protobuf.Empty + 5, // 8: gresponsewriterproto.Writer.Flush:output_type -> google.protobuf.Empty + 4, // 9: gresponsewriterproto.Writer.Hijack:output_type -> gresponsewriterproto.HijackResponse 6, // [6:10] is the sub-list for method output_type 2, // [2:6] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name @@ -653,54 +488,6 @@ func file_gresponsewriterproto_responsewriter_proto_init() { } } file_gresponsewriterproto_responsewriter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WriteHeaderResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gresponsewriterproto_responsewriter_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FlushRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gresponsewriterproto_responsewriter_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FlushResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gresponsewriterproto_responsewriter_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HijackRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_gresponsewriterproto_responsewriter_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HijackResponse); i { case 0: return &v.state @@ -719,7 +506,7 @@ func file_gresponsewriterproto_responsewriter_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gresponsewriterproto_responsewriter_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/api/proto/gresponsewriterproto/responsewriter_grpc.pb.go b/api/proto/gresponsewriterproto/responsewriter_grpc.pb.go index 70d8c7c6c197..81152b1975f1 100644 --- a/api/proto/gresponsewriterproto/responsewriter_grpc.pb.go +++ b/api/proto/gresponsewriterproto/responsewriter_grpc.pb.go @@ -11,6 +11,7 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -22,10 +23,15 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type WriterClient interface { + // Write writes the data to the connection as part of an HTTP reply Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) - WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*WriteHeaderResponse, error) - Flush(ctx context.Context, in *FlushRequest, opts ...grpc.CallOption) (*FlushResponse, error) - Hijack(ctx context.Context, in *HijackRequest, opts ...grpc.CallOption) (*HijackResponse, error) + // WriteHeader sends an HTTP response header with the provided + // status code + WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Flush is a no-op + Flush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Hijack lets the caller take over the connection + Hijack(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HijackResponse, error) } type writerClient struct { @@ -45,8 +51,8 @@ func (c *writerClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc return out, nil } -func (c *writerClient) WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*WriteHeaderResponse, error) { - out := new(WriteHeaderResponse) +func (c *writerClient) WriteHeader(ctx context.Context, in *WriteHeaderRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gresponsewriterproto.Writer/WriteHeader", in, out, opts...) if err != nil { return nil, err @@ -54,8 +60,8 @@ func (c *writerClient) WriteHeader(ctx context.Context, in *WriteHeaderRequest, return out, nil } -func (c *writerClient) Flush(ctx context.Context, in *FlushRequest, opts ...grpc.CallOption) (*FlushResponse, error) { - out := new(FlushResponse) +func (c *writerClient) Flush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/gresponsewriterproto.Writer/Flush", in, out, opts...) if err != nil { return nil, err @@ -63,7 +69,7 @@ func (c *writerClient) Flush(ctx context.Context, in *FlushRequest, opts ...grpc return out, nil } -func (c *writerClient) Hijack(ctx context.Context, in *HijackRequest, opts ...grpc.CallOption) (*HijackResponse, error) { +func (c *writerClient) Hijack(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HijackResponse, error) { out := new(HijackResponse) err := c.cc.Invoke(ctx, "/gresponsewriterproto.Writer/Hijack", in, out, opts...) if err != nil { @@ -76,10 +82,15 @@ func (c *writerClient) Hijack(ctx context.Context, in *HijackRequest, opts ...gr // All implementations must embed UnimplementedWriterServer // for forward compatibility type WriterServer interface { + // Write writes the data to the connection as part of an HTTP reply Write(context.Context, *WriteRequest) (*WriteResponse, error) - WriteHeader(context.Context, *WriteHeaderRequest) (*WriteHeaderResponse, error) - Flush(context.Context, *FlushRequest) (*FlushResponse, error) - Hijack(context.Context, *HijackRequest) (*HijackResponse, error) + // WriteHeader sends an HTTP response header with the provided + // status code + WriteHeader(context.Context, *WriteHeaderRequest) (*emptypb.Empty, error) + // Flush is a no-op + Flush(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + // Hijack lets the caller take over the connection + Hijack(context.Context, *emptypb.Empty) (*HijackResponse, error) mustEmbedUnimplementedWriterServer() } @@ -90,13 +101,13 @@ type UnimplementedWriterServer struct { func (UnimplementedWriterServer) Write(context.Context, *WriteRequest) (*WriteResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Write not implemented") } -func (UnimplementedWriterServer) WriteHeader(context.Context, *WriteHeaderRequest) (*WriteHeaderResponse, error) { +func (UnimplementedWriterServer) WriteHeader(context.Context, *WriteHeaderRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method WriteHeader not implemented") } -func (UnimplementedWriterServer) Flush(context.Context, *FlushRequest) (*FlushResponse, error) { +func (UnimplementedWriterServer) Flush(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Flush not implemented") } -func (UnimplementedWriterServer) Hijack(context.Context, *HijackRequest) (*HijackResponse, error) { +func (UnimplementedWriterServer) Hijack(context.Context, *emptypb.Empty) (*HijackResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Hijack not implemented") } func (UnimplementedWriterServer) mustEmbedUnimplementedWriterServer() {} @@ -149,7 +160,7 @@ func _Writer_WriteHeader_Handler(srv interface{}, ctx context.Context, dec func( } func _Writer_Flush_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FlushRequest) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -161,13 +172,13 @@ func _Writer_Flush_Handler(srv interface{}, ctx context.Context, dec func(interf FullMethod: "/gresponsewriterproto.Writer/Flush", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WriterServer).Flush(ctx, req.(*FlushRequest)) + return srv.(WriterServer).Flush(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _Writer_Hijack_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(HijackRequest) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -179,7 +190,7 @@ func _Writer_Hijack_Handler(srv interface{}, ctx context.Context, dec func(inter FullMethod: "/gresponsewriterproto.Writer/Hijack", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WriterServer).Hijack(ctx, req.(*HijackRequest)) + return srv.(WriterServer).Hijack(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } diff --git a/api/proto/gwriterproto/writer.pb.go b/api/proto/gwriterproto/writer.pb.go index 6893631018a4..2565273c2a19 100644 --- a/api/proto/gwriterproto/writer.pb.go +++ b/api/proto/gwriterproto/writer.pb.go @@ -25,6 +25,7 @@ type WriteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // payload is the write request in bytes Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` } @@ -72,9 +73,12 @@ type WriteResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Written int32 `protobuf:"varint,1,opt,name=written,proto3" json:"written,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` + // written is the length of payload in bytes + Written int32 `protobuf:"varint,1,opt,name=written,proto3" json:"written,omitempty"` + // error is an error message + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // errored is true if an error has been set + Errored bool `protobuf:"varint,3,opt,name=errored,proto3" json:"errored,omitempty"` } func (x *WriteResponse) Reset() { diff --git a/api/proto/gwriterproto/writer_grpc.pb.go b/api/proto/gwriterproto/writer_grpc.pb.go index 933764673e1a..521034c2b40d 100644 --- a/api/proto/gwriterproto/writer_grpc.pb.go +++ b/api/proto/gwriterproto/writer_grpc.pb.go @@ -22,6 +22,7 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type WriterClient interface { + // Write writes len(p) bytes from p to the underlying data stream. Write(ctx context.Context, in *WriteRequest, opts ...grpc.CallOption) (*WriteResponse, error) } @@ -46,6 +47,7 @@ func (c *writerClient) Write(ctx context.Context, in *WriteRequest, opts ...grpc // All implementations must embed UnimplementedWriterServer // for forward compatibility type WriterServer interface { + // Write writes len(p) bytes from p to the underlying data stream. Write(context.Context, *WriteRequest) (*WriteResponse, error) mustEmbedUnimplementedWriterServer() } diff --git a/api/proto/vmproto/vm.pb.go b/api/proto/vmproto/vm.pb.go index 6b34e5c67d18..a5e42b74c99e 100644 --- a/api/proto/vmproto/vm.pb.go +++ b/api/proto/vmproto/vm.pb.go @@ -27,22 +27,17 @@ type InitializeRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NetworkId uint32 `protobuf:"varint,1,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` - SubnetId []byte `protobuf:"bytes,2,opt,name=subnet_id,json=subnetId,proto3" json:"subnet_id,omitempty"` - ChainId []byte `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - NodeId []byte `protobuf:"bytes,4,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - XChainId []byte `protobuf:"bytes,5,opt,name=x_chain_id,json=xChainId,proto3" json:"x_chain_id,omitempty"` - AvaxAssetId []byte `protobuf:"bytes,6,opt,name=avax_asset_id,json=avaxAssetId,proto3" json:"avax_asset_id,omitempty"` - GenesisBytes []byte `protobuf:"bytes,7,opt,name=genesis_bytes,json=genesisBytes,proto3" json:"genesis_bytes,omitempty"` - UpgradeBytes []byte `protobuf:"bytes,8,opt,name=upgrade_bytes,json=upgradeBytes,proto3" json:"upgrade_bytes,omitempty"` - ConfigBytes []byte `protobuf:"bytes,9,opt,name=config_bytes,json=configBytes,proto3" json:"config_bytes,omitempty"` - DbServers []*VersionedDBServer `protobuf:"bytes,10,rep,name=db_servers,json=dbServers,proto3" json:"db_servers,omitempty"` - EngineServer uint32 `protobuf:"varint,11,opt,name=engine_server,json=engineServer,proto3" json:"engine_server,omitempty"` - KeystoreServer uint32 `protobuf:"varint,12,opt,name=keystore_server,json=keystoreServer,proto3" json:"keystore_server,omitempty"` - SharedMemoryServer uint32 `protobuf:"varint,13,opt,name=shared_memory_server,json=sharedMemoryServer,proto3" json:"shared_memory_server,omitempty"` - BcLookupServer uint32 `protobuf:"varint,14,opt,name=bc_lookup_server,json=bcLookupServer,proto3" json:"bc_lookup_server,omitempty"` - SnLookupServer uint32 `protobuf:"varint,15,opt,name=sn_lookup_server,json=snLookupServer,proto3" json:"sn_lookup_server,omitempty"` - AppSenderServer uint32 `protobuf:"varint,16,opt,name=app_sender_server,json=appSenderServer,proto3" json:"app_sender_server,omitempty"` + NetworkId uint32 `protobuf:"varint,1,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` + SubnetId []byte `protobuf:"bytes,2,opt,name=subnet_id,json=subnetId,proto3" json:"subnet_id,omitempty"` + ChainId []byte `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + NodeId []byte `protobuf:"bytes,4,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + XChainId []byte `protobuf:"bytes,5,opt,name=x_chain_id,json=xChainId,proto3" json:"x_chain_id,omitempty"` + AvaxAssetId []byte `protobuf:"bytes,6,opt,name=avax_asset_id,json=avaxAssetId,proto3" json:"avax_asset_id,omitempty"` + GenesisBytes []byte `protobuf:"bytes,7,opt,name=genesis_bytes,json=genesisBytes,proto3" json:"genesis_bytes,omitempty"` + UpgradeBytes []byte `protobuf:"bytes,8,opt,name=upgrade_bytes,json=upgradeBytes,proto3" json:"upgrade_bytes,omitempty"` + ConfigBytes []byte `protobuf:"bytes,9,opt,name=config_bytes,json=configBytes,proto3" json:"config_bytes,omitempty"` + DbServers []*VersionedDBServer `protobuf:"bytes,10,rep,name=db_servers,json=dbServers,proto3" json:"db_servers,omitempty"` + InitServer uint32 `protobuf:"varint,18,opt,name=init_server,json=initServer,proto3" json:"init_server,omitempty"` } func (x *InitializeRequest) Reset() { @@ -147,44 +142,9 @@ func (x *InitializeRequest) GetDbServers() []*VersionedDBServer { return nil } -func (x *InitializeRequest) GetEngineServer() uint32 { +func (x *InitializeRequest) GetInitServer() uint32 { if x != nil { - return x.EngineServer - } - return 0 -} - -func (x *InitializeRequest) GetKeystoreServer() uint32 { - if x != nil { - return x.KeystoreServer - } - return 0 -} - -func (x *InitializeRequest) GetSharedMemoryServer() uint32 { - if x != nil { - return x.SharedMemoryServer - } - return 0 -} - -func (x *InitializeRequest) GetBcLookupServer() uint32 { - if x != nil { - return x.BcLookupServer - } - return 0 -} - -func (x *InitializeRequest) GetSnLookupServer() uint32 { - if x != nil { - return x.SnLookupServer - } - return 0 -} - -func (x *InitializeRequest) GetAppSenderServer() uint32 { - if x != nil { - return x.AppSenderServer + return x.InitServer } return 0 } @@ -1969,7 +1929,7 @@ var file_vmproto_vm_proto_rawDesc = []byte{ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xed, 0x04, 0x0a, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x03, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, @@ -1992,295 +1952,282 @@ var file_vmproto_vm_proto_rawDesc = []byte{ 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x64, 0x62, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x44, 0x42, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x52, 0x09, 0x64, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, - 0x62, 0x63, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x62, 0x63, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6e, 0x5f, 0x6c, 0x6f, 0x6f, - 0x6b, 0x75, 0x70, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0e, 0x73, 0x6e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x70, 0x70, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x70, 0x70, - 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x27, 0x0a, 0x0f, - 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x22, 0x4a, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x44, 0x42, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x62, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x62, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x46, 0x0a, - 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x6d, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x72, 0x73, 0x22, 0x5c, 0x0a, 0x07, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6c, 0x6f, - 0x63, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x22, 0x8d, 0x01, 0x0a, 0x12, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x22, 0x29, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x8f, 0x01, 0x0a, - 0x12, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x72, 0x52, 0x09, 0x64, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4a, 0x04, 0x08, + 0x0b, 0x10, 0x12, 0x22, 0x27, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xd9, 0x01, 0x0a, + 0x12, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6c, + 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x64, 0x12, 0x35, 0x0a, + 0x17, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, + 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x4a, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x44, 0x42, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, + 0x09, 0x64, 0x62, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x64, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x46, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, + 0x0a, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x72, 0x52, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x1c, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, + 0x52, 0x08, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x5c, 0x0a, 0x07, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x21, 0x0a, + 0x0c, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x8d, 0x01, 0x0a, 0x12, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x29, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x73, + 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x21, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x93, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x21, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, - 0x64, 0x22, 0x93, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x26, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x50, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x2a, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x33, 0x0a, 0x13, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x26, + 0x0a, 0x14, 0x53, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x0e, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x2b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7d, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, - 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x0a, 0x0c, 0x41, 0x70, 0x70, - 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, + 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x22, 0x33, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x24, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x24, 0x0a, + 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x2a, 0x0a, 0x0e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, + 0x2b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7d, 0x0a, 0x0d, + 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x13, 0x41, + 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, + 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0e, 0x41, 0x70, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x39, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x73, 0x67, + 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x45, 0x0a, 0x10, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x2e, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, - 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x6d, 0x73, 0x67, 0x22, 0x45, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x0a, 0x13, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x13, - 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, - 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4e, 0x75, 0x6d, - 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6b, - 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x62, - 0x6c, 0x6b, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x34, 0x0a, 0x18, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x54, - 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x19, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, - 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x45, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a, - 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, - 0x5d, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x66, 0x61, 0x6d, 0x69, - 0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x69, 0x6f, 0x2e, - 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x52, 0x0e, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x32, 0xe2, - 0x0d, 0x0a, 0x02, 0x56, 0x4d, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x08, - 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x68, - 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x1f, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x55, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6c, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x6c, 0x6b, 0x49, + 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, + 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x4e, 0x75, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x74, 0x72, + 0x69, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, + 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6b, 0x73, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6b, 0x73, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, + 0x34, 0x0a, 0x18, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x54, 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, + 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, + 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x19, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, + 0x45, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, + 0x06, 0x62, 0x6c, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, + 0x6c, 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x5d, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, + 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, + 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, + 0x69, 0x6c, 0x69, 0x65, 0x73, 0x32, 0xe2, 0x0d, 0x0a, 0x02, 0x56, 0x4d, 0x12, 0x45, 0x0a, 0x0a, + 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x6d, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x18, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x25, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x79, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, + 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x19, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x19, 0x2e, 0x76, + 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x44, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, + 0x1c, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x41, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x76, 0x6d, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, + 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1a, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, + 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3f, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x2e, 0x76, 0x6d, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x46, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x50, + 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x76, 0x6d, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x48, + 0x0a, 0x10, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x41, - 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x47, + 0x6f, 0x73, 0x73, 0x69, 0x70, 0x12, 0x15, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, - 0x1a, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x6d, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0d, 0x53, 0x65, 0x74, - 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x76, 0x6d, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x06, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x48, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1b, + 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x6d, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, + 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x2e, 0x76, + 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x39, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x07, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x18, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x70, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x73, 0x67, 0x1a, + 0x79, 0x12, 0x4b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, + 0x0a, 0x11, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x11, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x48, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x76, 0x6d, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x17, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x3a, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x12, 0x15, - 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x47, 0x6f, 0x73, 0x73, - 0x69, 0x70, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, - 0x06, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x17, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x12, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4b, 0x0a, 0x0c, 0x47, 0x65, - 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x6d, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x11, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x76, - 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x50, 0x61, - 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x65, - 0x64, 0x50, 0x61, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x22, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x22, 0x2e, 0x76, 0x6d, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, - 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x6d, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x22, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x12, 0x22, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x41, 0x74, 0x48, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, + 0x73, 0x2f, 0x61, 0x76, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x67, 0x6f, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x6d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/api/vmproto/vm.proto b/api/vmproto/vm.proto index c606345549c0..ad0f0f01d640 100644 --- a/api/vmproto/vm.proto +++ b/api/vmproto/vm.proto @@ -5,7 +5,37 @@ import "google/protobuf/empty.proto"; import "io/prometheus/client/metrics.proto"; +service VM { + rpc Initialize(InitializeRequest) returns (InitializeResponse); + rpc SetState(SetStateRequest) returns (google.protobuf.Empty); + rpc Shutdown(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc CreateHandlers(google.protobuf.Empty) returns (CreateHandlersResponse); + rpc CreateStaticHandlers(google.protobuf.Empty) returns (CreateStaticHandlersResponse); + rpc Connected(ConnectedRequest) returns (google.protobuf.Empty); + rpc Disconnected(DisconnectedRequest) returns (google.protobuf.Empty); + rpc BuildBlock(google.protobuf.Empty) returns (BuildBlockResponse); + rpc ParseBlock(ParseBlockRequest) returns (ParseBlockResponse); + rpc GetBlock(GetBlockRequest) returns (GetBlockResponse); + rpc SetPreference(SetPreferenceRequest) returns (google.protobuf.Empty); + rpc Health(google.protobuf.Empty) returns (HealthResponse); + rpc Version(google.protobuf.Empty) returns (VersionResponse); + rpc AppRequest(AppRequestMsg) returns (google.protobuf.Empty); + rpc AppRequestFailed(AppRequestFailedMsg) returns (google.protobuf.Empty); + rpc AppResponse(AppResponseMsg) returns (google.protobuf.Empty); + rpc AppGossip(AppGossipMsg) returns (google.protobuf.Empty); + rpc Gather(google.protobuf.Empty) returns (GatherResponse); + rpc BlockVerify(BlockVerifyRequest) returns (BlockVerifyResponse); + rpc BlockAccept(BlockAcceptRequest) returns (google.protobuf.Empty); + rpc BlockReject(BlockRejectRequest) returns (google.protobuf.Empty); + rpc GetAncestors(GetAncestorsRequest) returns (GetAncestorsResponse); + rpc BatchedParseBlock(BatchedParseBlockRequest) returns (BatchedParseBlockResponse); + rpc VerifyHeightIndex(google.protobuf.Empty) returns (VerifyHeightIndexResponse); + rpc GetBlockIDAtHeight(GetBlockIDAtHeightRequest) returns (GetBlockIDAtHeightResponse); +} + message InitializeRequest { + // reserved fields ids have been removed and should not be reused + reserved 11 to 17; uint32 network_id = 1; bytes subnet_id = 2; bytes chain_id = 3; @@ -15,14 +45,8 @@ message InitializeRequest { bytes genesis_bytes = 7; bytes upgrade_bytes = 8; bytes config_bytes = 9; - repeated VersionedDBServer db_servers = 10; - uint32 engine_server = 11; - uint32 keystore_server = 12; - uint32 shared_memory_server = 13; - uint32 bc_lookup_server = 14; - uint32 sn_lookup_server = 15; - uint32 app_sender_server = 16; + uint32 init_server = 18; } message SetStateRequest { @@ -197,36 +221,3 @@ message GetBlockIDAtHeightResponse { message GatherResponse { repeated io.prometheus.client.MetricFamily metric_families = 1; } - -service VM { - rpc Initialize(InitializeRequest) returns (InitializeResponse); - rpc SetState(SetStateRequest) returns (google.protobuf.Empty); - rpc Shutdown(google.protobuf.Empty) returns (google.protobuf.Empty); - rpc CreateHandlers(google.protobuf.Empty) returns (CreateHandlersResponse); - rpc CreateStaticHandlers(google.protobuf.Empty) returns (CreateStaticHandlersResponse); - rpc Connected(ConnectedRequest) returns (google.protobuf.Empty); - rpc Disconnected(DisconnectedRequest) returns (google.protobuf.Empty); - rpc BuildBlock(google.protobuf.Empty) returns (BuildBlockResponse); - rpc ParseBlock(ParseBlockRequest) returns (ParseBlockResponse); - rpc GetBlock(GetBlockRequest) returns (GetBlockResponse); - rpc SetPreference(SetPreferenceRequest) returns (google.protobuf.Empty); - rpc Health(google.protobuf.Empty) returns (HealthResponse); - rpc Version(google.protobuf.Empty) returns (VersionResponse); - rpc AppRequest(AppRequestMsg) returns (google.protobuf.Empty); - rpc AppRequestFailed(AppRequestFailedMsg) returns (google.protobuf.Empty); - rpc AppResponse(AppResponseMsg) returns (google.protobuf.Empty); - rpc AppGossip(AppGossipMsg) returns (google.protobuf.Empty); - - rpc Gather(google.protobuf.Empty) returns (GatherResponse); - - rpc BlockVerify(BlockVerifyRequest) returns (BlockVerifyResponse); - rpc BlockAccept(BlockAcceptRequest) returns (google.protobuf.Empty); - rpc BlockReject(BlockRejectRequest) returns (google.protobuf.Empty); - - rpc GetAncestors(GetAncestorsRequest) returns (GetAncestorsResponse); - rpc BatchedParseBlock(BatchedParseBlockRequest) returns (BatchedParseBlockResponse); - - rpc VerifyHeightIndex(google.protobuf.Empty) returns (VerifyHeightIndexResponse); - rpc GetBlockIDAtHeight(GetBlockIDAtHeightRequest) returns (GetBlockIDAtHeightResponse); - -} diff --git a/chains/manager.go b/chains/manager.go index b8048f9d6f98..a7ac8744e080 100644 --- a/chains/manager.go +++ b/chains/manager.go @@ -322,11 +322,7 @@ func (m *manager) buildChain(chainParams ChainParameters, sb Subnet) (*chain, er if chainParams.ID != constants.PlatformChainID && vmID == constants.PlatformVMID { return nil, errCreatePlatformVM } - - primaryAlias, err := m.PrimaryAlias(chainParams.ID) - if err != nil { - primaryAlias = chainParams.ID.String() - } + primaryAlias := m.PrimaryAliasOrDefault(chainParams.ID) // Create the log and context of the chain chainLog, err := m.LogFactory.MakeChain(primaryAlias) @@ -663,10 +659,8 @@ func (m *manager) createAvalancheChain( handler.SetConsensus(engine) // Register health check for this chain - chainAlias, err := m.PrimaryAlias(ctx.ChainID) - if err != nil { - chainAlias = ctx.ChainID.String() - } + chainAlias := m.PrimaryAliasOrDefault(ctx.ChainID) + // Grab the context lock before calling the chain's health check check := health.CheckerFunc(func() (interface{}, error) { ctx.Lock.Lock() @@ -688,7 +682,7 @@ func (m *manager) createAvalancheChain( Name: chainAlias, Engine: engine, Handler: handler, - }, err + }, nil } // Create a linear chain using the Snowman consensus engine @@ -867,10 +861,7 @@ func (m *manager) createSnowmanChain( handler.SetConsensus(engine) // Register health checks - chainAlias, err := m.PrimaryAlias(ctx.ChainID) - if err != nil { - chainAlias = ctx.ChainID.String() - } + chainAlias := m.PrimaryAliasOrDefault(ctx.ChainID) check := health.CheckerFunc(func() (interface{}, error) { ctx.Lock.Lock() diff --git a/chains/mock_manager.go b/chains/mock_manager.go index bd2025687008..d4bd5bd0d2d0 100644 --- a/chains/mock_manager.go +++ b/chains/mock_manager.go @@ -20,6 +20,7 @@ func (mm MockManager) ForceCreateChain(ChainParameters) {} func (mm MockManager) AddRegistrant(Registrant) {} func (mm MockManager) Aliases(ids.ID) ([]string, error) { return nil, nil } func (mm MockManager) PrimaryAlias(ids.ID) (string, error) { return "", nil } +func (mm MockManager) PrimaryAliasOrDefault(ids.ID) string { return "" } func (mm MockManager) Alias(ids.ID, string) error { return nil } func (mm MockManager) RemoveAliases(ids.ID) {} func (mm MockManager) Shutdown() {} diff --git a/config/config.go b/config/config.go index b53c1f9edbef..ec6a7af2f01a 100644 --- a/config/config.go +++ b/config/config.go @@ -55,9 +55,7 @@ const ( ) var ( - deprecatedKeys = map[string]string{ - InboundConnUpgradeThrottlerMaxRecentKey: fmt.Sprintf("please use --%s to specify connection upgrade throttling", InboundThrottlerMaxConnsPerSecKey), - } + deprecatedKeys = map[string]string{} errInvalidStakerWeights = errors.New("staking weights must be positive") errStakingDisableOnPublicNetwork = errors.New("staking disabled on public network") @@ -136,13 +134,11 @@ func getConsensusConfig(v *viper.Viper) avalanche.Parameters { } func getLoggingConfig(v *viper.Viper) (logging.Config, error) { - loggingConfig, err := logging.DefaultConfig() - if err != nil { - return loggingConfig, err - } + loggingConfig := logging.DefaultConfig if v.IsSet(LogsDirKey) { loggingConfig.Directory = os.ExpandEnv(v.GetString(LogsDirKey)) } + var err error loggingConfig.LogLevel, err = logging.ToLevel(v.GetString(LogLevelKey)) if err != nil { return loggingConfig, err @@ -316,7 +312,7 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e config := network.Config{ // Throttling ThrottlerConfig: network.ThrottlerConfig{ - MaxIncomingConnsPerSec: maxInboundConnsPerSec, + MaxInboundConnsPerSec: maxInboundConnsPerSec, InboundConnUpgradeThrottlerConfig: throttling.InboundConnUpgradeThrottlerConfig{ UpgradeCooldown: upgradeCooldown, MaxRecentConnsUpgraded: maxRecentConnsUpgraded, @@ -348,7 +344,7 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e MaxPortionSendQueueBytesFull: v.GetFloat64(NetworkHealthMaxPortionSendQueueFillKey), MinConnectedPeers: v.GetUint(NetworkHealthMinPeersKey), MaxSendFailRate: v.GetFloat64(NetworkHealthMaxSendFailRateKey), - MaxSendFailRateHalflife: halflife, + SendFailRateHalflife: halflife, }, DialerConfig: dialer.Config{ @@ -357,17 +353,15 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e }, TimeoutConfig: network.TimeoutConfig{ - PeerAliasTimeout: v.GetDuration(PeerAliasTimeoutKey), - GetVersionTimeout: v.GetDuration(NetworkGetVersionTimeoutKey), PingPongTimeout: v.GetDuration(NetworkPingTimeoutKey), ReadHandshakeTimeout: v.GetDuration(NetworkReadHandshakeTimeoutKey), }, PeerListGossipConfig: network.PeerListGossipConfig{ - PeerListSize: v.GetUint32(NetworkPeerListSizeKey), - PeerListGossipFreq: v.GetDuration(NetworkPeerListGossipFreqKey), - PeerListGossipSize: v.GetUint32(NetworkPeerListGossipSizeKey), - PeerListStakerGossipFraction: v.GetUint32(NetworkPeerListStakerGossipFractionKey), + PeerListNumValidatorIPs: v.GetUint32(NetworkPeerListNumValidatorIPsKey), + PeerListValidatorGossipSize: v.GetUint32(NetworkPeerListValidatorGossipSizeKey), + PeerListNonValidatorGossipSize: v.GetUint32(NetworkPeerListNonValidatorGossipSizeKey), + PeerListGossipFreq: v.GetDuration(NetworkPeerListGossipFreqKey), }, DelayConfig: network.DelayConfig{ @@ -396,14 +390,8 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e return network.Config{}, fmt.Errorf("%s must be in [0,1]", NetworkHealthMaxPortionSendQueueFillKey) case config.DialerConfig.ConnectionTimeout < 0: return network.Config{}, fmt.Errorf("%q must be >= 0", OutboundConnectionTimeout) - case config.PeerAliasTimeout < 0: - return network.Config{}, fmt.Errorf("%q must be >= 0", PeerAliasTimeoutKey) case config.PeerListGossipFreq < 0: return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkPeerListGossipFreqKey) - case config.GetVersionTimeout < 0: - return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkGetVersionTimeoutKey) - case config.PeerListStakerGossipFraction < 1: - return network.Config{}, fmt.Errorf("%s must be >= 1", NetworkPeerListStakerGossipFractionKey) case config.MaxReconnectDelay < 0: return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkMaxReconnectDelayKey) case config.InitialReconnectDelay < 0: @@ -428,7 +416,6 @@ func getNetworkConfig(v *viper.Viper, halflife time.Duration) (network.Config, e func getBenchlistConfig(v *viper.Viper, alpha, k int) (benchlist.Config, error) { config := benchlist.Config{ Threshold: v.GetInt(BenchlistFailThresholdKey), - PeerSummaryEnabled: v.GetBool(BenchlistPeerSummaryEnabledKey), Duration: v.GetDuration(BenchlistDurationKey), MinimumFailingDuration: v.GetDuration(BenchlistMinFailingDurationKey), MaxPortion: (1.0 - (float64(alpha) / float64(k))) / 3.0, @@ -452,8 +439,17 @@ func getBootstrapConfig(v *viper.Viper, networkID uint32) (node.BootstrapConfig, BootstrapAncestorsMaxContainersReceived: int(v.GetUint(BootstrapAncestorsMaxContainersReceivedKey)), } + ipsSet := v.IsSet(BootstrapIPsKey) + idsSet := v.IsSet(BootstrapIDsKey) + if ipsSet && !idsSet { + return node.BootstrapConfig{}, fmt.Errorf("set %q but didn't set %q", BootstrapIPsKey, BootstrapIDsKey) + } + if !ipsSet && idsSet { + return node.BootstrapConfig{}, fmt.Errorf("set %q but didn't set %q", BootstrapIDsKey, BootstrapIPsKey) + } + bootstrapIPs, bootstrapIDs := genesis.SampleBeacons(networkID, 5) - if v.IsSet(BootstrapIPsKey) { + if ipsSet { bootstrapIPs = strings.Split(v.GetString(BootstrapIPsKey), ",") } for _, ip := range bootstrapIPs { @@ -467,7 +463,7 @@ func getBootstrapConfig(v *viper.Viper, networkID uint32) (node.BootstrapConfig, config.BootstrapIPs = append(config.BootstrapIPs, addr) } - if v.IsSet(BootstrapIDsKey) { + if idsSet { bootstrapIDs = strings.Split(v.GetString(BootstrapIDsKey), ",") } for _, id := range bootstrapIDs { @@ -480,6 +476,13 @@ func getBootstrapConfig(v *viper.Viper, networkID uint32) (node.BootstrapConfig, } config.BootstrapIDs = append(config.BootstrapIDs, nodeID) } + + lenIPs := len(config.BootstrapIPs) + lenIDs := len(config.BootstrapIDs) + if lenIPs != lenIDs { + return node.BootstrapConfig{}, fmt.Errorf("expected the number of bootstrapIPs (%d) to match the number of bootstrapIDs (%d)", lenIPs, lenIDs) + } + return config, nil } diff --git a/config/flags.go b/config/flags.go index 9699ec1fe48f..3927dafd1f06 100644 --- a/config/flags.go +++ b/config/flags.go @@ -114,15 +114,16 @@ func addNodeFlags(fs *flag.FlagSet) { // Peer List Gossip gossipHelpMsg := fmt.Sprintf( - "Gossip [%s] peers to [%s] peers every [%s]", - NetworkPeerListSizeKey, - NetworkPeerListGossipSizeKey, + "Gossip [%s] validator IPs to [%s] validators and [%s] non-validators every [%s]", + NetworkPeerListNumValidatorIPsKey, + NetworkPeerListValidatorGossipSizeKey, + NetworkPeerListNonValidatorGossipSizeKey, NetworkPeerListGossipFreqKey, ) - fs.Uint(NetworkPeerListSizeKey, 20, gossipHelpMsg) - fs.Uint(NetworkPeerListGossipSizeKey, 50, gossipHelpMsg) + fs.Uint(NetworkPeerListNumValidatorIPsKey, 20, gossipHelpMsg) + fs.Uint(NetworkPeerListValidatorGossipSizeKey, 25, gossipHelpMsg) + fs.Uint(NetworkPeerListNonValidatorGossipSizeKey, 25, gossipHelpMsg) fs.Duration(NetworkPeerListGossipFreqKey, time.Minute, gossipHelpMsg) - fs.Uint(NetworkPeerListStakerGossipFractionKey, 2, fmt.Sprintf("1 of each %s peer list messages gossiped will be to validators", NetworkPeerListStakerGossipFractionKey)) // Public IP Resolution fs.String(PublicIPKey, "", "Public IP of this node for P2P communication. If empty, try to discover with NAT. Ignored if dynamic-public-ip is non-empty") @@ -131,7 +132,6 @@ func addNodeFlags(fs *flag.FlagSet) { // Inbound Connection Throttling fs.Duration(InboundConnUpgradeThrottlerCooldownKey, 10*time.Second, "Upgrade an inbound connection from a given IP at most once per this duration. If 0, don't rate-limit inbound connection upgrades") - fs.Int(InboundConnUpgradeThrottlerMaxRecentKey, 5120, "DEPRECATED") // Deprecated starting in v1.6.0. TODO remove in future release. fs.Float64(InboundThrottlerMaxConnsPerSecKey, 256, "Max number of inbound connections to accept (from all peers) per second") // Outbound Connection Throttling fs.Uint(OutboundConnectionThrottlingRps, 50, "Make at most this number of outgoing peer connection attempts per second") @@ -143,21 +143,17 @@ func addNodeFlags(fs *flag.FlagSet) { fs.Duration(NetworkMaximumInboundTimeoutKey, 10*time.Second, "Maximum timeout value of an inbound message. Defines duration within which an incoming message must be fulfilled. Incoming messages containing deadline higher than this value will be overridden with this value.") fs.Duration(NetworkTimeoutHalflifeKey, 5*time.Minute, "Halflife of average network response time. Higher value --> network timeout is less volatile. Can't be 0") fs.Float64(NetworkTimeoutCoefficientKey, 2, "Multiplied by average network response time to get the network timeout. Must be >= 1") - fs.Duration(NetworkGetVersionTimeoutKey, 10*time.Second, "Timeout for waiting GetVersion response from peers in handshake") fs.Duration(NetworkReadHandshakeTimeoutKey, 15*time.Second, "Timeout value for reading handshake messages") fs.Duration(NetworkPingTimeoutKey, constants.DefaultPingPongTimeout, "Timeout value for Ping-Pong with a peer") fs.Duration(NetworkPingFrequencyKey, constants.DefaultPingFrequency, "Frequency of pinging other peers") fs.Bool(NetworkCompressionEnabledKey, true, "If true, compress certain outbound messages. This node will be able to parse compressed inbound messages regardless of this flag's value") fs.Duration(NetworkMaxClockDifferenceKey, time.Minute, "Max allowed clock difference value between this node and peers") - fs.Bool(NetworkAllowPrivateIPsKey, true, "Allows the node to connect peers with private IPs") + fs.Bool(NetworkAllowPrivateIPsKey, true, "Allows the node to initiate outbound connection attempts to peers with private IPs") fs.Bool(NetworkRequireValidatorToConnectKey, false, "If true, this node will only maintain a connection with another node if this node is a validator, the other node is a validator, or the other node is a beacon") - // Peer alias configuration - fs.Duration(PeerAliasTimeoutKey, 10*time.Minute, "How often the node will attempt to connect to an IP address previously associated with a peer (i.e. a peer alias)") // Benchlist fs.Int(BenchlistFailThresholdKey, 10, "Number of consecutive failed queries before benchlisting a node") - fs.Bool(BenchlistPeerSummaryEnabledKey, false, "Enables peer specific query latency metrics") fs.Duration(BenchlistDurationKey, 15*time.Minute, "Max amount of time a peer is benchlisted after surpassing the threshold") fs.Duration(BenchlistMinFailingDurationKey, 2*time.Minute+30*time.Second, "Minimum amount of time messages to a peer must be failing before the peer is benched") diff --git a/config/keys.go b/config/keys.go index 02591f9c0b54..67624c56e25d 100644 --- a/config/keys.go +++ b/config/keys.go @@ -37,7 +37,6 @@ const ( DynamicUpdateDurationKey = "dynamic-update-duration" DynamicPublicIPResolverKey = "dynamic-public-ip" InboundConnUpgradeThrottlerCooldownKey = "inbound-connection-throttling-cooldown" - InboundConnUpgradeThrottlerMaxRecentKey = "inbound-connection-throttling-max-recent" // Deprecated starting in v1.6.0. TODO remove in a future release. InboundThrottlerMaxConnsPerSecKey = "inbound-connection-throttling-max-conns-per-sec" OutboundConnectionThrottlingRps = "outbound-connection-throttling-rps" OutboundConnectionTimeout = "outbound-connection-timeout" @@ -76,12 +75,11 @@ const ( NetworkHealthMaxPortionSendQueueFillKey = "network-health-max-portion-send-queue-full" NetworkHealthMaxSendFailRateKey = "network-health-max-send-fail-rate" NetworkHealthMaxOutstandingDurationKey = "network-health-max-outstanding-request-duration" - NetworkPeerListSizeKey = "network-peer-list-size" - NetworkPeerListGossipSizeKey = "network-peer-list-gossip-size" + NetworkPeerListNumValidatorIPsKey = "network-peer-list-num-validator-ips" + NetworkPeerListValidatorGossipSizeKey = "network-peer-list-validator-gossip-size" + NetworkPeerListNonValidatorGossipSizeKey = "network-peer-list-non-validator-gossip-size" NetworkPeerListGossipFreqKey = "network-peer-list-gossip-frequency" - NetworkPeerListStakerGossipFractionKey = "network-peer-list-staker-gossip-fraction" NetworkInitialReconnectDelayKey = "network-initial-reconnect-delay" - NetworkGetVersionTimeoutKey = "network-get-version-timeout" NetworkReadHandshakeTimeoutKey = "network-read-handshake-timeout" NetworkPingTimeoutKey = "network-ping-timeout" NetworkPingFrequencyKey = "network-ping-frequency" @@ -91,7 +89,6 @@ const ( NetworkAllowPrivateIPsKey = "network-allow-private-ips" NetworkRequireValidatorToConnectKey = "network-require-validator-to-connect" BenchlistFailThresholdKey = "benchlist-fail-threshold" - BenchlistPeerSummaryEnabledKey = "benchlist-peer-summary-enabled" BenchlistDurationKey = "benchlist-duration" BenchlistMinFailingDurationKey = "benchlist-min-failing-duration" BuildDirKey = "build-dir" @@ -135,7 +132,6 @@ const ( HealthCheckAveragerHalflifeKey = "health-check-averager-halflife" RetryBootstrapKey = "bootstrap-retry-enabled" RetryBootstrapWarnFrequencyKey = "bootstrap-retry-warn-frequency" - PeerAliasTimeoutKey = "peer-alias-timeout" PluginModeKey = "plugin-mode-enabled" BootstrapBeaconConnectionTimeoutKey = "bootstrap-beacon-connection-timeout" BootstrapMaxTimeGetAncestorsKey = "boostrap-max-time-get-ancestors" diff --git a/database/leveldb/db.go b/database/leveldb/db.go index 22750e5c1a43..143a17b10990 100644 --- a/database/leveldb/db.go +++ b/database/leveldb/db.go @@ -118,6 +118,13 @@ type config struct { // // The default value is 10. CompactionTotalSizeMultiplier float64 `json:"compactionTotalSizeMultiplier"` + // DisableSeeksCompaction allows disabling 'seeks triggered compaction'. + // The purpose of 'seeks triggered compaction' is to optimize database so + // that 'level seeks' can be minimized, however this might generate many + // small compaction which may not preferable. + // + // The default is true. + DisableSeeksCompaction bool `json:"disableSeeksCompaction"` // OpenFilesCacheCapacity defines the capacity of the open files caching. // Use -1 for zero, this has same effect as specifying NoCacher to OpenFilesCacher. // @@ -138,6 +145,7 @@ type config struct { func New(file string, configBytes []byte, log logging.Logger) (database.Database, error) { parsedConfig := config{ BlockCacheCapacity: BlockCacheSize, + DisableSeeksCompaction: true, OpenFilesCacheCapacity: HandleCap, WriteBuffer: WriteBufferSize / 2, FilterBitsPerKey: BitsPerKey, @@ -165,6 +173,7 @@ func New(file string, configBytes []byte, log logging.Logger) (database.Database CompactionTableSizeMultiplier: parsedConfig.CompactionTableSizeMultiplier, CompactionTotalSize: parsedConfig.CompactionTotalSize, CompactionTotalSizeMultiplier: parsedConfig.CompactionTotalSizeMultiplier, + DisableSeeksCompaction: parsedConfig.DisableSeeksCompaction, OpenFilesCacheCapacity: parsedConfig.OpenFilesCacheCapacity, WriteBuffer: parsedConfig.WriteBuffer, Filter: filter.NewBloomFilter(parsedConfig.FilterBitsPerKey), diff --git a/database/linkeddb/linkeddb.go b/database/linkeddb/linkeddb.go index 7ab7f8c68f59..efa2856cf0df 100644 --- a/database/linkeddb/linkeddb.go +++ b/database/linkeddb/linkeddb.go @@ -34,7 +34,7 @@ type LinkedDB interface { } type linkedDB struct { - // lock ensure that this datastructure handles its thread safety correctly. + // lock ensure that this data structure handles its thread safety correctly. lock sync.RWMutex cacheLock sync.Mutex diff --git a/go.mod b/go.mod index 7bc54354114a..edda6f731353 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,8 @@ go 1.16 require ( github.com/Microsoft/go-winio v0.4.16 github.com/NYTimes/gziphandler v1.1.1 - github.com/ava-labs/avalanche-network-runner v1.0.5 - github.com/ava-labs/coreth v0.8.6-rc.1 + github.com/ava-labs/avalanche-network-runner v1.0.6 + github.com/ava-labs/coreth v0.8.7-rc.2 github.com/btcsuite/btcutil v1.0.2 github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200627015759-01fd2de07837 github.com/golang-jwt/jwt v3.2.1+incompatible @@ -31,7 +31,6 @@ require ( github.com/jackpal/go-nat-pmp v1.0.2 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/linxGnu/grocksdb v1.6.34 - github.com/mitchellh/go-homedir v1.1.0 github.com/mr-tron/base58 v1.2.0 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d github.com/onsi/ginkgo/v2 v2.1.0 @@ -49,6 +48,7 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac gonum.org/v1/gonum v0.9.1 + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa google.golang.org/grpc v1.43.0 google.golang.org/protobuf v1.27.1 gotest.tools v2.2.0+incompatible diff --git a/go.sum b/go.sum index bdb3b9a8d670..6d5b00bf3c65 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,9 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/VictoriaMetrics/fastcache v1.9.0 h1:oMwsS6c8abz98B7ytAewQ7M1ZN/Im/iwKoE1euaFvhs= +github.com/VictoriaMetrics/fastcache v1.9.0/go.mod h1:otoTS3xu+6IzF/qByjqzjp3rTuzM3Qf0ScU1UTj97iU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -114,18 +115,22 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/ava-labs/avalanche-network-runner v1.0.5 h1:0iwuoEvpnMxzFSEBjeSu0PJZZWIQq3Mdc6AJ10fiMac= -github.com/ava-labs/avalanche-network-runner v1.0.5/go.mod h1:lwuxQh2y0HtmeKASjBC9O1Sip1oiNeFuK6Ma3PxCH4Y= +github.com/ava-labs/avalanche-network-runner v1.0.6 h1:hph6/kHOWjgAxiQG8MZO7ZOvPsNzkjvu/cqnCMW0jSw= +github.com/ava-labs/avalanche-network-runner v1.0.6/go.mod h1:lwuxQh2y0HtmeKASjBC9O1Sip1oiNeFuK6Ma3PxCH4Y= github.com/ava-labs/avalanchego v1.5.3/go.mod h1:mfsT8C6mfjCEAjE90q8aNh9BqGEFNBd2dgIr23RaePw= github.com/ava-labs/avalanchego v1.7.4-0.20211222191707-3163be793cc9/go.mod h1:YPIP9GyzXKjscsWlvbPWvV0TQcgV+0t8rMe8sLvc0xQ= github.com/ava-labs/avalanchego v1.7.4-0.20220113231534-ccb8039d74b9/go.mod h1:hm1T3Nui/qRALnNngXmC+lWeTEPtYyPBKxaWMWuRBrs= github.com/ava-labs/avalanchego v1.7.4/go.mod h1:+xnmwjlkeNHhs0v5p2yvdv7cw0jgyq4SyU9O+S4EUPs= github.com/ava-labs/avalanchego v1.7.5-0.20220202014036-7c45dd1e2377/go.mod h1:sUhn77bV5EBGXwqcFz1FzFs94MR6SRgrU+Ob7lb+pC4= +github.com/ava-labs/avalanchego v1.7.7-rc.1/go.mod h1:a5R0Zar2qRmaz2ukZs2zCocU/HvOIYrkKB506rsQTks= +github.com/ava-labs/avalanchego v1.7.7-rc.4/go.mod h1:ZFuoVdAoCKS3Q4NFBKsR7TVCGyLvdbgwUkmkGE6jmi0= github.com/ava-labs/avalanchego-operator v0.0.0-20211115144351-99f07d2570bf/go.mod h1:RTkVkouQ0HBSBVSFAQCUviXPU7Iuxoy+Fui3ykmD9HA= github.com/ava-labs/coreth v0.8.4-rc.1/go.mod h1:bP2Atm7pCJdx8fwzsPT3xU/kWOdHFklpja7aRNT++Qo= github.com/ava-labs/coreth v0.8.4-rc.3/go.mod h1:9TgpLJVY9ot6RV8Lh66F356S4MfalvaL3sAqw4+miTU= -github.com/ava-labs/coreth v0.8.6-rc.1 h1:6zkFThycTyBSnOMBb0lyMfMs1XCR0twp3ihA7onIyGE= github.com/ava-labs/coreth v0.8.6-rc.1/go.mod h1:D60h55zuIQu6/dc7nWpXNyk3xakq3xsWMFuumttcpqw= +github.com/ava-labs/coreth v0.8.7-rc.1/go.mod h1:E9yfBswaDbV3WeFeX90wS0eRAfeW7WPbxgbc4b0KJlk= +github.com/ava-labs/coreth v0.8.7-rc.2 h1:QrQiAIl4zGRy8eOQWXubm2Hs+zXJLggjItZa8q35Yww= +github.com/ava-labs/coreth v0.8.7-rc.2/go.mod h1:HC8D4Ei7PNRzpPpqGC/M08YYeBp1kogJBVcWIfGseVA= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= @@ -258,8 +263,9 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw= github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= +github.com/ethereum/go-ethereum v1.10.16 h1:3oPrumn0bCW/idjcxMn5YYVCdK7VzJYIvwGZUGLEaoc= +github.com/ethereum/go-ethereum v1.10.16/go.mod h1:Anj6cxczl+AHy63o4X9O8yWNHuN5wMpfb8MAnHkWn7Y= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -444,6 +450,7 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -564,6 +571,7 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -639,7 +647,6 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -1168,8 +1175,9 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= @@ -1466,6 +1474,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ids/aliases.go b/ids/aliases.go index 357ca5eb9f97..31b083a21a2c 100644 --- a/ids/aliases.go +++ b/ids/aliases.go @@ -27,6 +27,7 @@ type AliaserWriter interface { type Aliaser interface { AliaserReader AliaserWriter + PrimaryAliasOrDefault(id ID) string } type aliaser struct { @@ -65,6 +66,15 @@ func (a *aliaser) PrimaryAlias(id ID) (string, error) { return aliases[0], nil } +// PrimaryAliasOrDefault returns the first alias of [id], or ID string as default +func (a *aliaser) PrimaryAliasOrDefault(id ID) string { + alias, err := a.PrimaryAlias(id) + if err != nil { + return id.String() + } + return alias +} + // Aliases returns the aliases of an ID func (a *aliaser) Aliases(id ID) ([]string, error) { a.lock.RLock() diff --git a/ids/aliases_test.go b/ids/aliases_test.go index 2d5311777e01..ed295d66387e 100644 --- a/ids/aliases_test.go +++ b/ids/aliases_test.go @@ -16,3 +16,23 @@ func TestAliaser(t *testing.T) { test(assert, aliaser, aliaser) } } + +func TestPrimaryAliasOrDefaultTest(t *testing.T) { + assert := assert.New(t) + aliaser := NewAliaser() + id1 := ID{'J', 'a', 'm', 'e', 's', ' ', 'G', 'o', 'r', 'd', 'o', 'n'} + id2 := ID{'B', 'r', 'u', 'c', 'e', ' ', 'W', 'a', 'y', 'n', 'e'} + err := aliaser.Alias(id2, "Batman") + assert.NoError(err) + + err = aliaser.Alias(id2, "Dark Knight") + assert.NoError(err) + + res := aliaser.PrimaryAliasOrDefault(id1) + assert.Equal(res, id1.String()) + + expected := "Batman" + res = aliaser.PrimaryAliasOrDefault(id2) + assert.NoError(err) + assert.Equal(expected, res) +} diff --git a/message/builder_test.go b/message/builder_test.go index f21add0f0690..1f152db6e157 100644 --- a/message/builder_test.go +++ b/message/builder_test.go @@ -35,21 +35,8 @@ func init() { UncompressingBuilder = NewOutboundBuilder(codec, false /*compress*/) } -func TestBuildGetVersion(t *testing.T) { - msg, err := UncompressingBuilder.GetVersion() - assert.NoError(t, err) - assert.NotNil(t, msg) - assert.Equal(t, GetVersion, msg.Op()) - - parsedMsg, err := TestCodec.Parse(msg.Bytes(), dummyNodeID, dummyOnFinishedHandling) - assert.NoError(t, err) - assert.NotNil(t, parsedMsg) - assert.Equal(t, GetVersion, parsedMsg.Op()) -} - func TestBuildVersion(t *testing.T) { networkID := uint32(12345) - nodeID := uint32(56789) myTime := uint64(time.Now().Unix()) ip := utils.IPDesc{ IP: net.IPv4(1, 2, 3, 4), @@ -62,7 +49,6 @@ func TestBuildVersion(t *testing.T) { subnetIDs := [][]byte{subnetID[:]} msg, err := UncompressingBuilder.Version( networkID, - nodeID, myTime, ip, myVersion, @@ -80,7 +66,6 @@ func TestBuildVersion(t *testing.T) { assert.NotNil(t, parsedMsg) assert.Equal(t, Version, parsedMsg.Op()) assert.EqualValues(t, networkID, parsedMsg.Get(NetworkID)) - assert.EqualValues(t, nodeID, parsedMsg.Get(NodeID)) assert.EqualValues(t, myTime, parsedMsg.Get(MyTime)) assert.EqualValues(t, ip, parsedMsg.Get(IP)) assert.EqualValues(t, myVersion, parsedMsg.Get(VersionStr)) @@ -89,18 +74,6 @@ func TestBuildVersion(t *testing.T) { assert.EqualValues(t, subnetIDs, parsedMsg.Get(TrackedSubnets)) } -func TestBuildGetPeerList(t *testing.T) { - msg, err := UncompressingBuilder.GetPeerList() - assert.NoError(t, err) - assert.NotNil(t, msg) - assert.Equal(t, GetPeerList, msg.Op()) - - parsedMsg, err := TestCodec.Parse(msg.Bytes(), dummyNodeID, dummyOnFinishedHandling) - assert.NoError(t, err) - assert.NotNil(t, parsedMsg) - assert.Equal(t, GetPeerList, parsedMsg.Op()) -} - func TestBuildGetAcceptedFrontier(t *testing.T) { chainID := ids.Empty.Prefix(0) requestID := uint32(5) diff --git a/message/codec_test.go b/message/codec_test.go index 8b5202d2315f..c29a2aa0358a 100644 --- a/message/codec_test.go +++ b/message/codec_test.go @@ -53,10 +53,10 @@ func TestCodecParseExtraSpace(t *testing.T) { codec, err := NewCodecWithMemoryPool("", prometheus.NewRegistry(), 2*units.MiB, 10*time.Second) assert.NoError(t, err) - _, err = codec.Parse([]byte{byte(GetVersion), 0x00, 0x00}, dummyNodeID, dummyOnFinishedHandling) + _, err = codec.Parse([]byte{byte(Ping), 0x00, 0x00}, dummyNodeID, dummyOnFinishedHandling) assert.Error(t, err) - _, err = codec.Parse([]byte{byte(GetVersion), 0x00, 0x01}, dummyNodeID, dummyOnFinishedHandling) + _, err = codec.Parse([]byte{byte(Ping), 0x00, 0x01}, dummyNodeID, dummyOnFinishedHandling) assert.Error(t, err) } @@ -96,10 +96,6 @@ func TestCodecPackParseGzip(t *testing.T) { cert := &x509.Certificate{} msgs := []inboundMessage{ - { - op: GetVersion, - fields: map[Field]interface{}{}, - }, { op: Version, fields: map[Field]interface{}{ @@ -113,10 +109,6 @@ func TestCodecPackParseGzip(t *testing.T) { TrackedSubnets: [][]byte{id[:]}, }, }, - { - op: GetPeerList, - fields: map[Field]interface{}{}, - }, { op: PeerList, fields: map[Field]interface{}{ diff --git a/message/fields.go b/message/fields.go index 1ad5df51a59e..1ff97564f2de 100644 --- a/message/fields.go +++ b/message/fields.go @@ -14,7 +14,7 @@ type Field uint32 const ( VersionStr Field = iota // Used in handshake NetworkID // Used in handshake - NodeID // Used in handshake + NodeID // TODO: remove NodeID. Used in handshake MyTime // Used in handshake IP // Used in handshake Peers // Used in handshake diff --git a/message/ops.go b/message/ops.go index 71946d8da9df..e34c0a1558d2 100644 --- a/message/ops.go +++ b/message/ops.go @@ -9,14 +9,18 @@ type Op byte // Types of messages that may be sent between nodes // Note: If you add a new parseable Op below, you must also add it to ops // (declared below) +// +// "_" are used in places where old message types were defined that are no +// longer supported. When new messages are introduced these values are typically +// safe to reuse. const ( // Handshake: - GetVersion Op = iota - _ - GetPeerList + _ Op = iota // Used to be a GetVersion message + _ // Used to be a Version message + _ // Used to be a GetPeerList message Pong Ping - _ + _ // Used to be a Pong message // Bootstrapping: GetAcceptedFrontier AcceptedFrontier @@ -31,7 +35,7 @@ const ( PullQuery Chits // Handshake / peer gossiping - _ + _ // Used to be a Version message PeerList Version // Application level: @@ -55,9 +59,7 @@ const ( var ( HandshakeOps = []Op{ - GetVersion, Version, - GetPeerList, PeerList, Ping, Pong, @@ -174,12 +176,11 @@ var ( // Defines the messages that can be sent/received with this network messages = map[Op][]Field{ // Handshake: - GetVersion: {}, - Version: {NetworkID, NodeID, MyTime, IP, VersionStr, VersionTime, SigBytes, TrackedSubnets}, - GetPeerList: {}, - PeerList: {SignedPeers}, - Ping: {}, - Pong: {Uptime}, + // TODO: remove NodeID from the Version message + Version: {NetworkID, NodeID, MyTime, IP, VersionStr, VersionTime, SigBytes, TrackedSubnets}, + PeerList: {SignedPeers}, + Ping: {}, + Pong: {Uptime}, // Bootstrapping: GetAcceptedFrontier: {ChainID, RequestID, Deadline}, AcceptedFrontier: {ChainID, RequestID, ContainerIDs}, @@ -211,12 +212,8 @@ func (op Op) Compressible() bool { func (op Op) String() string { switch op { - case GetVersion: - return "get_version" case Version: return "version" - case GetPeerList: - return "get_peerlist" case PeerList: return "peerlist" case Ping: diff --git a/message/outbound_msg_builder.go b/message/outbound_msg_builder.go index 562990246a3f..aad96c33c724 100644 --- a/message/outbound_msg_builder.go +++ b/message/outbound_msg_builder.go @@ -16,11 +16,8 @@ var _ OutboundMsgBuilder = &outMsgBuilder{} // with a reference count of 1. Once the reference count hits 0, the message // bytes should no longer be accessed. type OutboundMsgBuilder interface { - GetVersion() (OutboundMessage, error) - Version( - networkID, - nodeID uint32, + networkID uint32, myTime uint64, ip utils.IPDesc, myVersion string, @@ -29,9 +26,10 @@ type OutboundMsgBuilder interface { trackedSubnets []ids.ID, ) (OutboundMessage, error) - GetPeerList() (OutboundMessage, error) - - PeerList(peers []utils.IPCertDesc) (OutboundMessage, error) + PeerList( + peers []utils.IPCertDesc, + bypassThrottling bool, + ) (OutboundMessage, error) Ping() (OutboundMessage, error) @@ -141,18 +139,8 @@ func NewOutboundBuilder(c Codec, enableCompression bool) OutboundMsgBuilder { } } -func (b *outMsgBuilder) GetVersion() (OutboundMessage, error) { - return b.c.Pack( - GetVersion, - nil, - GetVersion.Compressible(), // GetVersion messages can't be compressed - false, - ) -} - func (b *outMsgBuilder) Version( - networkID, - nodeID uint32, + networkID uint32, myTime uint64, ip utils.IPDesc, myVersion string, @@ -169,7 +157,7 @@ func (b *outMsgBuilder) Version( Version, map[Field]interface{}{ NetworkID: networkID, - NodeID: nodeID, + NodeID: uint32(0), MyTime: myTime, IP: ip, VersionStr: myVersion, @@ -178,27 +166,18 @@ func (b *outMsgBuilder) Version( TrackedSubnets: subnetIDBytes, }, Version.Compressible(), // Version Messages can't be compressed - false, - ) -} - -func (b *outMsgBuilder) GetPeerList() (OutboundMessage, error) { - return b.c.Pack( - GetPeerList, - nil, - GetPeerList.Compressible(), // GetPeerList messages can't be compressed - false, + true, ) } -func (b *outMsgBuilder) PeerList(peers []utils.IPCertDesc) (OutboundMessage, error) { +func (b *outMsgBuilder) PeerList(peers []utils.IPCertDesc, bypassThrottling bool) (OutboundMessage, error) { return b.c.Pack( PeerList, map[Field]interface{}{ SignedPeers: peers, }, b.compress && PeerList.Compressible(), // PeerList messages may be compressed - false, + bypassThrottling, ) } diff --git a/network/certs_test.go b/network/certs_test.go new file mode 100644 index 000000000000..6b67640fcfcf --- /dev/null +++ b/network/certs_test.go @@ -0,0 +1,39 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "crypto/tls" + "sync" + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/staking" +) + +var ( + certLock sync.Mutex + tlsCerts []*tls.Certificate + tlsConfigs []*tls.Config +) + +func getTLS(t *testing.T, index int) (ids.ShortID, *tls.Certificate, *tls.Config) { + certLock.Lock() + defer certLock.Unlock() + + for len(tlsCerts) <= index { + cert, err := staking.NewTLSCert() + if err != nil { + t.Fatal(err) + } + tlsConfig := peer.TLSConfig(*cert) + + tlsCerts = append(tlsCerts, cert) + tlsConfigs = append(tlsConfigs, tlsConfig) + } + + cert := tlsCerts[index] + return peer.CertToID(cert.Leaf), cert, tlsConfigs[index] +} diff --git a/network/config.go b/network/config.go new file mode 100644 index 000000000000..d66eb351aeac --- /dev/null +++ b/network/config.go @@ -0,0 +1,147 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "crypto" + "crypto/tls" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/dialer" + "github.com/ava-labs/avalanchego/network/throttling" + "github.com/ava-labs/avalanchego/snow/uptime" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils" +) + +// HealthConfig describes parameters for network layer health checks. +type HealthConfig struct { + // MinConnectedPeers is the minimum number of peers that the network should + // be connected to to be considered healthy. + MinConnectedPeers uint `json:"minConnectedPeers"` + + // MaxTimeSinceMsgReceived is the maximum amount of time since the network + // last received a message to be considered healthy. + MaxTimeSinceMsgReceived time.Duration `json:"maxTimeSinceMsgReceived"` + + // MaxTimeSinceMsgSent is the maximum amount of time since the network last + // sent a message to be considered healthy. + MaxTimeSinceMsgSent time.Duration `json:"maxTimeSinceMsgSent"` + + // MaxPortionSendQueueBytesFull is the maximum percentage of the pending + // send byte queue that should be used for the network to be considered + // healthy. Should be in (0,1]. + MaxPortionSendQueueBytesFull float64 `json:"maxPortionSendQueueBytesFull"` + + // MaxSendFailRate is the maximum percentage of send attempts that should be + // failing for the network to be considered healthy. This does not include + // send attempts that were not made due to benching. Should be in [0,1]. + MaxSendFailRate float64 `json:"maxSendFailRate"` + + // SendFailRateHalflife is the halflife of the averager used to calculate + // the send fail rate percentage. Should be > 0. Larger values mean that the + // fail rate is affected less by recently dropped messages. + SendFailRateHalflife time.Duration `json:"sendFailRateHalflife"` +} + +type PeerListGossipConfig struct { + // PeerListNumValidatorIPs is the number of validator IPs to gossip in every + // gossip event. + PeerListNumValidatorIPs uint32 `json:"peerListNumValidatorIPs"` + + // PeerListValidatorGossipSize is the number of validators to gossip the IPs + // to in every IP gossip event. + PeerListValidatorGossipSize uint32 `json:"peerListValidatorGossipSize"` + + // PeerListNonValidatorGossipSize is the number of non-validators to gossip + // the IPs to in every IP gossip event. + PeerListNonValidatorGossipSize uint32 `json:"peerListNonValidatorGossipSize"` + + // PeerListGossipFreq is the frequency that this node will attempt to gossip + // signed IPs to its peers. + PeerListGossipFreq time.Duration `json:"peerListGossipFreq"` +} + +type TimeoutConfig struct { + // PingPongTimeout is the maximum amount of time to wait for a Pong response + // from a peer we sent a Ping to. + PingPongTimeout time.Duration `json:"pingPongTimeout"` + + // ReadHandshakeTimeout is the maximum amount of time to wait for the peer's + // connection upgrade to finish before starting the p2p handshake. + ReadHandshakeTimeout time.Duration `json:"readHandshakeTimeout"` +} + +type DelayConfig struct { + // InitialReconnectDelay is the minimum amount of time the node will delay a + // reconnection to a peer. This value is used to start the exponential + // backoff. + InitialReconnectDelay time.Duration `json:"initialReconnectDelay"` + + // MaxReconnectDelay is the maximum amount of time the node will delay a + // reconnection to a peer. + MaxReconnectDelay time.Duration `json:"maxReconnectDelay"` +} + +type ThrottlerConfig struct { + InboundConnUpgradeThrottlerConfig throttling.InboundConnUpgradeThrottlerConfig `json:"inboundConnUpgradeThrottlerConfig"` + InboundMsgThrottlerConfig throttling.InboundMsgThrottlerConfig `json:"inboundMsgThrottlerConfig"` + OutboundMsgThrottlerConfig throttling.MsgByteThrottlerConfig `json:"outboundMsgThrottlerConfig"` + MaxInboundConnsPerSec float64 `json:"maxInboundConnsPerSec"` +} + +type Config struct { + HealthConfig `json:"healthConfig"` + PeerListGossipConfig `json:"peerListGossipConfig"` + TimeoutConfig `json:"timeoutConfigs"` + DelayConfig `json:"delayConfig"` + ThrottlerConfig ThrottlerConfig `json:"throttlerConfig"` + + DialerConfig dialer.Config `json:"dialerConfig"` + TLSConfig *tls.Config `json:"-"` + + Namespace string `json:"namespace"` + MyNodeID ids.ShortID `json:"myNodeID"` + MyIP utils.DynamicIPDesc `json:"myIP"` + NetworkID uint32 `json:"networkID"` + MaxClockDifference time.Duration `json:"maxClockDifference"` + PingFrequency time.Duration `json:"pingFrequency"` + AllowPrivateIPs bool `json:"allowPrivateIPs"` + + // CompressionEnabled will compress available outbound messages when set to + // true. + CompressionEnabled bool `json:"compressionEnabled"` + + // TLSKey is this node's TLS key that is used to sign IPs. + TLSKey crypto.Signer `json:"-"` + + // WhitelistedSubnets of the node. + WhitelistedSubnets ids.Set `json:"whitelistedSubnets"` + Beacons validators.Set `json:"beacons"` + + // Validators are the current validators in the Avalanche network + Validators validators.Manager `json:"validators"` + + UptimeCalculator uptime.Calculator `json:"-"` + + // UptimeMetricFreq marks how frequently this node will recalculate the + // observed average uptime metrics. + UptimeMetricFreq time.Duration `json:"uptimeMetricFreq"` + + // UptimeRequirement is the fraction of time a validator must be online and + // responsive for us to vote that they should receive a staking reward. + UptimeRequirement float64 `json:"uptimeRequirement"` + + // RequireValidatorToConnect require that all connections must have at least + // one validator between the 2 peers. This can be useful to enable if the + // node wants to connect to the minimum number of nodes without impacting + // the network negatively. + RequireValidatorToConnect bool `json:"requireValidatorToConnect"` + + // MaximumInboundMessageTimeout is the maximum deadline duration in a + // message. Messages sent by clients setting values higher than this value + // will be reset to this value. + MaximumInboundMessageTimeout time.Duration `json:"maximumInboundMessageTimeout"` +} diff --git a/network/conn_test.go b/network/conn_test.go new file mode 100644 index 000000000000..79afe2a7c2d4 --- /dev/null +++ b/network/conn_test.go @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "net" +) + +var _ net.Conn = &testConn{} + +type testConn struct { + net.Conn + + localAddr net.Addr + remoteAddr net.Addr +} + +func (c *testConn) LocalAddr() net.Addr { + return c.localAddr +} + +func (c *testConn) RemoteAddr() net.Addr { + return c.remoteAddr +} diff --git a/network/dialer_test.go b/network/dialer_test.go new file mode 100644 index 000000000000..ea194ef27f8e --- /dev/null +++ b/network/dialer_test.go @@ -0,0 +1,83 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "context" + "errors" + "net" + + "github.com/ava-labs/avalanchego/network/dialer" + "github.com/ava-labs/avalanchego/utils" +) + +var ( + errRefused = errors.New("connection refused") + + _ dialer.Dialer = &testDialer{} +) + +type testDialer struct { + // maps [ip.String] to a listener + listeners map[string]*testListener +} + +func newTestDialer() *testDialer { + return &testDialer{ + listeners: make(map[string]*testListener), + } +} + +func (d *testDialer) NewListener() (utils.DynamicIPDesc, *testListener) { + ip := utils.NewDynamicIPDesc( + net.IPv6loopback, + uint16(len(d.listeners)), + ) + staticIP := ip.IP() + listener := newTestListener(staticIP) + d.AddListener(staticIP, listener) + return ip, listener +} + +func (d *testDialer) AddListener(ip utils.IPDesc, listener *testListener) { + d.listeners[ip.String()] = listener +} + +func (d *testDialer) Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error) { + listener, ok := d.listeners[ip.String()] + if !ok { + return nil, errRefused + } + serverConn, clientConn := net.Pipe() + server := &testConn{ + Conn: serverConn, + localAddr: &net.TCPAddr{ + IP: net.IPv6loopback, + Port: 0, + }, + remoteAddr: &net.TCPAddr{ + IP: net.IPv6loopback, + Port: 1, + }, + } + client := &testConn{ + Conn: clientConn, + localAddr: &net.TCPAddr{ + IP: net.IPv6loopback, + Port: 2, + }, + remoteAddr: &net.TCPAddr{ + IP: net.IPv6loopback, + Port: 3, + }, + } + select { + case listener.inbound <- server: + return client, nil + case <-ctx.Done(): + return nil, ctx.Err() + case <-listener.closed: + return nil, errRefused + } +} diff --git a/network/handler_test.go b/network/handler_test.go new file mode 100644 index 000000000000..bdc68c4e26a8 --- /dev/null +++ b/network/handler_test.go @@ -0,0 +1,30 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/version" +) + +var _ router.ExternalHandler = &testHandler{} + +type testHandler struct { + router.InboundHandler + ConnectedF func(nodeID ids.ShortID, nodeVersion version.Application) + DisconnectedF func(nodeID ids.ShortID) +} + +func (h *testHandler) Connected(id ids.ShortID, nodeVersion version.Application) { + if h.ConnectedF != nil { + h.ConnectedF(id, nodeVersion) + } +} + +func (h *testHandler) Disconnected(id ids.ShortID) { + if h.DisconnectedF != nil { + h.DisconnectedF(id) + } +} diff --git a/network/health.go b/network/health.go deleted file mode 100644 index 9037bad5da39..000000000000 --- a/network/health.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package network - -import "time" - -// HealthConfig describes parameters for network layer health checks. -type HealthConfig struct { - // Must be connected to at least this many peers to be considered healthy - MinConnectedPeers uint `json:"minConnectedPeers"` - - // Must have received a message from the network within this duration - // to be considered healthy. Must be positive - MaxTimeSinceMsgReceived time.Duration `json:"maxTimeSinceMsgReceived"` - - // Must have sent a message over the network within this duration - // to be considered healthy. Must be positive - MaxTimeSinceMsgSent time.Duration `json:"maxTimeSinceMsgSent"` - - // If greater than this portion of the pending send byte queue is full, - // will report unhealthy. Must be in (0,1] - MaxPortionSendQueueBytesFull float64 `json:"maxPortionSendQueueBytesFull"` - - // If greater than this portion of the attempts to send a message to a peer - // fail, will return unhealthy. Does not include send attempts that were not - // made due to benching. Must be in [0,1] - MaxSendFailRate float64 `json:"maxSendFailRate"` - - // Halflife of averager used to calculate the send fail rate - // Must be > 0. - // Larger value --> Drop rate affected less by recent messages - MaxSendFailRateHalflife time.Duration `json:"maxSendFailRateHalflife"` -} diff --git a/network/ip_signer.go b/network/ip_signer.go new file mode 100644 index 000000000000..d70ee3ea1847 --- /dev/null +++ b/network/ip_signer.go @@ -0,0 +1,80 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "crypto" + "sync" + + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/timer/mockable" +) + +// ipSigner will return a signedIP for the current value of our dynamic IP. +type ipSigner struct { + ip *utils.DynamicIPDesc + clock *mockable.Clock + signer crypto.Signer + + // Must be held while accessing [signedIP] + signedIPLock sync.RWMutex + // Note that the values in [*signedIP] are constants and can be inspected + // without holding [signedIPLock]. + signedIP *peer.SignedIP +} + +func newIPSigner( + ip *utils.DynamicIPDesc, + clock *mockable.Clock, + signer crypto.Signer, +) *ipSigner { + return &ipSigner{ + ip: ip, + clock: clock, + signer: signer, + } +} + +// getSignedIP returns the signedIP of the current value of the provided +// dynamicIP. If the dynamicIP hasn't changed since the prior call to +// getSignedIP, then the same [SignedIP] will be returned. +// +// It's safe for multiple goroutines to concurrently call getSignedIP. +func (s *ipSigner) getSignedIP() (*peer.SignedIP, error) { + // Optimistically, the IP should already be signed. By grabbing a read lock + // here we enable full concurrency of new connections. + s.signedIPLock.RLock() + signedIP := s.signedIP + s.signedIPLock.RUnlock() + ip := s.ip.IP() + if signedIP != nil && signedIP.IP.IP.Equal(ip) { + return signedIP, nil + } + + // If our current IP hasn't been signed yet - then we should sign it. + s.signedIPLock.Lock() + defer s.signedIPLock.Unlock() + + // It's possible that multiple threads read [n.signedIP] as incorrect at the + // same time, we should verify that we are the first thread to attempt to + // update it. + signedIP = s.signedIP + if signedIP != nil && signedIP.IP.IP.Equal(ip) { + return signedIP, nil + } + + // We should now sign our new IP at the current timestamp. + unsignedIP := peer.UnsignedIP{ + IP: ip, + Timestamp: s.clock.Unix(), + } + signedIP, err := unsignedIP.Sign(s.signer) + if err != nil { + return nil, err + } + + s.signedIP = signedIP + return s.signedIP, nil +} diff --git a/network/ip_signer_test.go b/network/ip_signer_test.go new file mode 100644 index 000000000000..9067fde43a2e --- /dev/null +++ b/network/ip_signer_test.go @@ -0,0 +1,59 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "crypto" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/timer/mockable" +) + +func TestIPSigner(t *testing.T) { + assert := assert.New(t) + + dynIP := utils.NewDynamicIPDesc( + net.IPv6loopback, + 0, + ) + clock := mockable.Clock{} + clock.Set(time.Unix(10, 0)) + + tlsCert, err := staking.NewTLSCert() + assert.NoError(err) + + key := tlsCert.PrivateKey.(crypto.Signer) + + s := newIPSigner(&dynIP, &clock, key) + + signedIP1, err := s.getSignedIP() + assert.NoError(err) + assert.EqualValues(dynIP.IP(), signedIP1.IP.IP) + assert.EqualValues(10, signedIP1.IP.Timestamp) + + clock.Set(time.Unix(11, 0)) + + signedIP2, err := s.getSignedIP() + assert.NoError(err) + assert.EqualValues(dynIP.IP(), signedIP2.IP.IP) + assert.EqualValues(10, signedIP2.IP.Timestamp) + assert.EqualValues(signedIP1.Signature, signedIP2.Signature) + + dynIP.Update(utils.IPDesc{ + IP: net.IPv6loopback, + Port: 1, + }) + + signedIP3, err := s.getSignedIP() + assert.NoError(err) + assert.EqualValues(dynIP.IP(), signedIP3.IP.IP) + assert.EqualValues(11, signedIP3.IP.Timestamp) + assert.NotEqualValues(signedIP2.Signature, signedIP3.Signature) +} diff --git a/network/listener_test.go b/network/listener_test.go new file mode 100644 index 000000000000..d9019307148b --- /dev/null +++ b/network/listener_test.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "errors" + "net" + + "github.com/ava-labs/avalanchego/utils" +) + +var ( + errClosed = errors.New("closed") + + _ net.Listener = &testListener{} +) + +type testListener struct { + ip utils.IPDesc + inbound chan net.Conn + closed chan struct{} +} + +func newTestListener(ip utils.IPDesc) *testListener { + return &testListener{ + ip: ip, + inbound: make(chan net.Conn), + closed: make(chan struct{}), + } +} + +func (l *testListener) Accept() (net.Conn, error) { + select { + case c := <-l.inbound: + return c, nil + case <-l.closed: + return nil, errClosed + } +} + +func (l *testListener) Close() error { + close(l.closed) + return nil +} + +func (l *testListener) Addr() net.Addr { + return &net.TCPAddr{ + IP: l.ip.IP, + Port: int(l.ip.Port), + } +} diff --git a/network/metrics.go b/network/metrics.go index df902a429007..923bd058b4c3 100644 --- a/network/metrics.go +++ b/network/metrics.go @@ -4,165 +4,98 @@ package network import ( - "fmt" - "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/avalanchego/message" - "github.com/ava-labs/avalanchego/utils/metric" "github.com/ava-labs/avalanchego/utils/wrappers" ) -type messageMetrics struct { - receivedBytes, sentBytes, numSent, numFailed, numReceived prometheus.Counter - savedReceivedBytes, savedSentBytes metric.Averager -} - -func newMessageMetrics(op message.Op, namespace string, metrics prometheus.Registerer, errs *wrappers.Errs) *messageMetrics { - msg := &messageMetrics{ - numSent: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: fmt.Sprintf("%s_sent", op), - Help: fmt.Sprintf("Number of %s messages sent over the network", op), - }), - numFailed: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: fmt.Sprintf("%s_failed", op), - Help: fmt.Sprintf("Number of %s messages that failed to be sent over the network", op), - }), - numReceived: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: fmt.Sprintf("%s_received", op), - Help: fmt.Sprintf("Number of %s messages received from the network", op), - }), - receivedBytes: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: fmt.Sprintf("%s_received_bytes", op), - Help: fmt.Sprintf("Number of bytes of %s messages received from the network", op), - }), - sentBytes: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: fmt.Sprintf("%s_sent_bytes", op), - Help: fmt.Sprintf("Size of bytes of %s messages received from the network", op), - }), - } - errs.Add( - metrics.Register(msg.numSent), - metrics.Register(msg.numFailed), - metrics.Register(msg.numReceived), - metrics.Register(msg.receivedBytes), - metrics.Register(msg.sentBytes), - ) - - if op.Compressible() { - msg.savedReceivedBytes = metric.NewAveragerWithErrs( - namespace, - fmt.Sprintf("%s_compression_saved_received_bytes", op), - fmt.Sprintf("bytes saved (not received) due to compression of %s messages", op), - metrics, - errs, - ) - msg.savedSentBytes = metric.NewAveragerWithErrs( - namespace, - fmt.Sprintf("%s_compression_saved_sent_bytes", op), - fmt.Sprintf("bytes saved (not sent) due to compression of %s messages", op), - metrics, - errs, - ) - } else { - msg.savedReceivedBytes = metric.NewNoAverager() - msg.savedSentBytes = metric.NewNoAverager() - } - return msg -} - type metrics struct { numPeers prometheus.Gauge + numTracked prometheus.Gauge timeSinceLastMsgSent prometheus.Gauge timeSinceLastMsgReceived prometheus.Gauge sendQueuePortionFull prometheus.Gauge sendFailRate prometheus.Gauge - failedToParse prometheus.Counter connected prometheus.Counter disconnected prometheus.Counter inboundConnRateLimited prometheus.Counter inboundConnAllowed prometheus.Counter nodeUptimeWeightedAverage prometheus.Gauge nodeUptimeRewardingStake prometheus.Gauge - - messageMetrics map[message.Op]*messageMetrics } -func (m *metrics) initialize(namespace string, registerer prometheus.Registerer) error { - m.numPeers = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "peers", - Help: "Number of network peers", - }) - m.timeSinceLastMsgReceived = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "time_since_last_msg_received", - Help: "Time (in ns) since the last msg was received", - }) - m.timeSinceLastMsgSent = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "time_since_last_msg_sent", - Help: "Time (in ns) since the last msg was sent", - }) - m.sendQueuePortionFull = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "send_queue_portion_full", - Help: "Percentage of use in Send Queue", - }) - m.sendFailRate = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "send_fail_rate", - Help: "Portion of messages that recently failed to be sent over the network", - }) - m.failedToParse = prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "msgs_failed_to_parse", - Help: "Number of messages that could not be parsed or were invalidly formed", - }) - m.connected = prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "times_connected", - Help: "Times this node successfully completed a handshake with a peer", - }) - m.disconnected = prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "times_disconnected", - Help: "Times this node disconnected from a peer it had completed a handshake with", - }) - m.inboundConnAllowed = prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "inbound_conn_throttler_allowed", - Help: "Times this node allowed (attempted to upgrade) an inbound connection", - }) - m.inboundConnRateLimited = prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "inbound_conn_throttler_rate_limited", - Help: "Times this node rejected an inbound connection due to rate-limiting", - }) - m.nodeUptimeWeightedAverage = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "node_uptime_weighted_average", - Help: "This node's uptime average weighted by observing peer stakes", - }) - m.nodeUptimeRewardingStake = prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "node_uptime_rewarding_stake", - Help: "The percentage of total stake which thinks this node is eligible for rewards", - }) +func newMetrics(namespace string, registerer prometheus.Registerer) (*metrics, error) { + m := &metrics{ + numPeers: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "peers", + Help: "Number of network peers", + }), + numTracked: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "tracked", + Help: "Number of currently tracked IPs attempting to be connected to", + }), + timeSinceLastMsgReceived: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "time_since_last_msg_received", + Help: "Time (in ns) since the last msg was received", + }), + timeSinceLastMsgSent: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "time_since_last_msg_sent", + Help: "Time (in ns) since the last msg was sent", + }), + sendQueuePortionFull: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "send_queue_portion_full", + Help: "Percentage of use in Send Queue", + }), + sendFailRate: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "send_fail_rate", + Help: "Portion of messages that recently failed to be sent over the network", + }), + connected: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "times_connected", + Help: "Times this node successfully completed a handshake with a peer", + }), + disconnected: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "times_disconnected", + Help: "Times this node disconnected from a peer it had completed a handshake with", + }), + inboundConnAllowed: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "inbound_conn_throttler_allowed", + Help: "Times this node allowed (attempted to upgrade) an inbound connection", + }), + inboundConnRateLimited: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "inbound_conn_throttler_rate_limited", + Help: "Times this node rejected an inbound connection due to rate-limiting", + }), + nodeUptimeWeightedAverage: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "node_uptime_weighted_average", + Help: "This node's uptime average weighted by observing peer stakes", + }), + nodeUptimeRewardingStake: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "node_uptime_rewarding_stake", + Help: "The percentage of total stake which thinks this node is eligible for rewards", + }), + } errs := wrappers.Errs{} errs.Add( registerer.Register(m.numPeers), + registerer.Register(m.numTracked), registerer.Register(m.timeSinceLastMsgReceived), registerer.Register(m.timeSinceLastMsgSent), registerer.Register(m.sendQueuePortionFull), registerer.Register(m.sendFailRate), - registerer.Register(m.failedToParse), registerer.Register(m.connected), registerer.Register(m.disconnected), registerer.Register(m.inboundConnAllowed), @@ -170,10 +103,5 @@ func (m *metrics) initialize(namespace string, registerer prometheus.Registerer) registerer.Register(m.nodeUptimeWeightedAverage), registerer.Register(m.nodeUptimeRewardingStake), ) - - m.messageMetrics = make(map[message.Op]*messageMetrics, len(message.ExternalOps)) - for _, op := range message.ExternalOps { - m.messageMetrics[op] = newMessageMetrics(op, namespace, registerer, &errs) - } - return errs.Err + return m, errs.Err } diff --git a/network/network.go b/network/network.go index 247b2f50351d..9e709dbea6c8 100644 --- a/network/network.go +++ b/network/network.go @@ -5,19 +5,14 @@ package network import ( "context" - "crypto" - "crypto/tls" "errors" "fmt" - "io" - "math/rand" "net" "strings" "sync" "sync/atomic" "time" - cryptorand "crypto/rand" gomath "math" "github.com/prometheus/client_golang/prometheus" @@ -26,68 +21,67 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network/dialer" + "github.com/ava-labs/avalanchego/network/peer" "github.com/ava-labs/avalanchego/network/throttling" "github.com/ava-labs/avalanchego/snow/networking/benchlist" "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/snow/networking/sender" - "github.com/ava-labs/avalanchego/snow/uptime" - "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/json" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/utils/sampler" - "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" ) +const ( + ConnectedPeersKey = "connectedPeers" + TimeSinceLastMsgReceivedKey = "timeSinceLastMsgReceived" + TimeSinceLastMsgSentKey = "timeSinceLastMsgSent" + SendFailRateKey = "sendFailRate" +) + var ( - errNetworkClosed = errors.New("network closed") - errPeerIsMyself = errors.New("peer is myself") errNoPrimaryValidators = errors.New("no default subnet validators") _ Network = &network{} ) -func init() { rand.Seed(time.Now().UnixNano()) } - // Network defines the functionality of the networking library. type Network interface { // All consensus messages can be sent through this interface. Thread safety // must be managed internally in the network. sender.ExternalSender - // Close this network and all existing connections it has. Thread safety - // must be managed internally to the network. Calling close multiple times - // will return a nil error. - io.Closer + // Has a health check + health.Checker + + peer.Network + + // StartClose this network and all existing connections it has. Calling + // StartClose multiple times is handled gracefully. + StartClose() // Should only be called once, will run until either a fatal error occurs, - // or the network is closed. Returns a non-nil error. + // or the network is closed. Dispatch() error - // Attempt to connect to this IP. Thread safety must be managed internally - // to the network. The network will never stop attempting to connect to this - // IP. - TrackIP(ip utils.IPDesc) + // WantsConnection returns true if this node is willing to attempt to + // connect to the provided nodeID. If the node is attempting to connect to + // the minimum number of peers, then it should only connect if the peer is a + // validator or beacon. + WantsConnection(ids.ShortID) bool - // Attempt to connect to this node ID at IP. Thread safety must be managed - // internally to the network. - Track(ip utils.IPDesc, nodeID ids.ShortID) + // Attempt to connect to this IP. The network will never stop attempting to + // connect to this IP. + ManuallyTrack(nodeID ids.ShortID, ip utils.IPDesc) - // Returns the description of the specified [nodeIDs] this network is currently - // connected to externally or all nodes this network is connected to if [nodeIDs] - // is empty. Thread safety must be managed internally to the network. - Peers(nodeIDs []ids.ShortID) []PeerInfo - - // Return the IP of the node - IP() utils.IPDesc + // PeerInfo returns information about peers. If [nodeIDs] is empty, returns + // info about all peers that have finished the handshake. Otherwise, returns + // info about the peers in [nodeIDs] that have finished the handshake. + PeerInfo(nodeIDs []ids.ShortID) []peer.Info NodeUptime() (UptimeResult, bool) - - // Has a health check - health.Checker } type UptimeResult struct { @@ -96,168 +90,54 @@ type UptimeResult struct { } type network struct { - config *Config - // The metrics that this network tracks - metrics metrics - // Unix time at which last message of any type received over network - // Must only be accessed atomically - lastMsgReceivedTime int64 - // Unix time at which last message of any type sent over network - // Must only be accessed atomically - lastMsgSentTime int64 - // Keeps track of the percentage of sends that fail - sendFailRateCalculator math.Averager - log logging.Logger - currentIP utils.DynamicIPDesc - versionCompatibility version.Compatibility - parser version.ApplicationParser - listener net.Listener - dialer dialer.Dialer - serverUpgrader Upgrader - clientUpgrader Upgrader - router router.Router // router must be thread safe - // This field just makes sure we don't connect to ourselves when TLS is - // disabled. So, cryptographically secure random number generation isn't - // used here. - dummyNodeID uint32 - clock mockable.Clock - mc message.Creator - - stateLock sync.RWMutex - closed utils.AtomicBool - - // May contain peers that we have not finished the handshake with. - peers peersData - - // disconnectedIPs, connectedIPs, peerAliasIPs, and myIPs - // are maps with utils.IPDesc.String() keys that are used to determine if - // we should attempt to dial an IP. [stateLock] should be held - // whenever accessing one of these maps. - disconnectedIPs map[string]struct{} // set of IPs we are attempting to connect to - connectedIPs map[string]struct{} // set of IPs we have open connections with - peerAliasIPs map[string]struct{} // set of alternate IPs we've reached existing peers at - // TODO: bound the size of [myIPs] to avoid DoS. LRU caching would be ideal - myIPs map[string]struct{} // set of IPs that resulted in my ID. - - // retryDelay is a map with utils.IPDesc.String() keys that is used to track - // the backoff delay we should wait before attempting to dial an IP address - // again. - retryDelay map[string]time.Duration + config *Config + peerConfig *peer.Config + metrics *metrics + // Signs my IP so I can send my signed IP address to other nodes in Version + // messages + ipSigner *ipSigner + + // Limits the number of connection attempts based on IP. + inboundConnUpgradeThrottler throttling.InboundConnUpgradeThrottler + // Listens for and accepts new inbound connections + listener net.Listener + // Makes new outbound connections + dialer dialer.Dialer + // Does TLS handshakes for inbound connections + serverUpgrader peer.Upgrader + // Does TLS handshakes for outbound connections + clientUpgrader peer.Upgrader // ensures the close of the network only happens once. closeOnce sync.Once + onClose chan struct{} - hasMasked bool - maskedValidators ids.ShortSet - - benchlistManager benchlist.Manager - - // [lastTimestampLock] should be held when touching [lastVersionIP], - // [lastVersionTimestamp], and [lastVersionSignature] - timeForIPLock sync.Mutex - // The IP for ourself that we included in the most recent Version message we - // sent. - lastVersionIP utils.IPDesc - // The timestamp we included in the most recent Version message we sent. - lastVersionTimestamp uint64 - // The signature we included in the most recent Version message we sent. - lastVersionSignature []byte - - // Node ID --> Latest IP/timestamp of this node from a Version or PeerList message - // The values in this map all have [signature] == nil - // A peer is removed from this map when [connected] is called with the peer as the argument - // TODO also remove from this map when the peer leaves the validator set - latestPeerIP map[ids.ShortID]signedPeerIP - - // Node ID --> Function to execute to stop trying to dial the node. - // A node is present in this map if and only if we are actively - // trying to dial the node. - connAttempts sync.Map - - // Rate-limits incoming messages - inboundMsgThrottler throttling.InboundMsgThrottler - inboundConnUpgradeThrottler throttling.InboundConnUpgradeThrottler - - // Rate-limits outgoing messages - outboundMsgThrottler throttling.OutboundMsgThrottler -} - -type PeerListGossipConfig struct { - PeerListSize uint32 `json:"peerListSize"` - PeerListGossipSize uint32 `json:"peerListGossipSize"` - PeerListStakerGossipFraction uint32 `json:"peerListStakerGossipFraction"` - PeerListGossipFreq time.Duration `json:"peerListGossipFreq"` -} - -type TimeoutConfig struct { - GetVersionTimeout time.Duration `json:"getVersionTimeout"` - PingPongTimeout time.Duration `json:"pingPongTimeout"` - ReadHandshakeTimeout time.Duration `json:"readHandshakeTimeout"` - // peerAliasTimeout is the age a peer alias must - // be before we attempt to release it (so that we - // attempt to dial the IP again if gossiped to us). - PeerAliasTimeout time.Duration `json:"peerAliasTimeout"` -} - -type DelayConfig struct { - InitialReconnectDelay time.Duration `json:"initialReconnectDelay"` - MaxReconnectDelay time.Duration `json:"maxReconnectDelay"` -} - -type ThrottlerConfig struct { - InboundConnUpgradeThrottlerConfig throttling.InboundConnUpgradeThrottlerConfig `json:"inboundConnUpgradeThrottlerConfig"` - InboundMsgThrottlerConfig throttling.InboundMsgThrottlerConfig `json:"inboundMsgThrottlerConfig"` - OutboundMsgThrottlerConfig throttling.MsgByteThrottlerConfig `json:"outboundMsgThrottlerConfig"` - MaxIncomingConnsPerSec float64 `json:"maxIncomingConnsPerSec"` -} - -type Config struct { - HealthConfig `json:"healthConfig"` - PeerListGossipConfig `json:"peerListGossipConfig"` - TimeoutConfig `json:"timeoutConfigs"` - DelayConfig `json:"delayConfig"` - ThrottlerConfig ThrottlerConfig `json:"throttlerConfig"` - - DialerConfig dialer.Config `json:"dialerConfig"` - TLSConfig *tls.Config `json:"-"` - - Namespace string `json:"namespace"` - MyNodeID ids.ShortID `json:"myNodeID"` - MyIP utils.DynamicIPDesc `json:"myIP"` - NetworkID uint32 `json:"networkID"` - MaxClockDifference time.Duration `json:"maxClockDifference"` - PingFrequency time.Duration `json:"pingFrequency"` - AllowPrivateIPs bool `json:"allowPrivateIPs"` - CompressionEnabled bool `json:"compressionEnabled"` - // This node's TLS key - TLSKey crypto.Signer `json:"-"` - // WhitelistedSubnets of the node - WhitelistedSubnets ids.Set `json:"whitelistedSubnets"` - Beacons validators.Set `json:"beacons"` - // Current validators in the Avalanche network - Validators validators.Manager `json:"validators"` - UptimeCalculator uptime.Calculator `json:"-"` - UptimeMetricFreq time.Duration `json:"uptimeMetricFreq"` - UptimeRequirement float64 `json:"uptimeRequirement"` - - // Require that all connections must have at least one validator between the - // 2 peers. This can be useful to enable if the node wants to connect to the - // minimum number of nodes without impacting the network negatively. - RequireValidatorToConnect bool `json:"requireValidatorToConnect"` - - // Maximum deadline duration in a message. Messages sent by clients setting - // values higher than this value will be reset to this value. - MaximumInboundMessageTimeout time.Duration -} + sendFailRateCalculator math.Averager -// peerElement holds onto the peer object as a result of helper functions -type peerElement struct { - // the peer, if it wasn't a peer when we cloned the list this value will be - // nil - peer *peer - // this is the validator id for the peer, we pass back to the caller for - // logging purposes - id ids.ShortID + peersLock sync.RWMutex + // trackedIPs contains the set of IPs that we are currently attempting to + // connect to. An entry is added to this set when we first start attempting + // to connect to the peer. An entry is deleted from this set once we have + // finished the handshake. + trackedIPs map[ids.ShortID]*trackedIP + connectingPeers peer.Set + connectedPeers peer.Set + closing bool + + // router is notified about all peer [Connected] and [Disconnected] events + // as well as all non-handshake peer messages. + // + // It is ensured that [Connected] and [Disconnected] are called in + // consistent ways. Specifically, the a peer starts in the disconnected + // state and the network can change the peer's state from disconnected to + // connected and back. + // + // It is ensured that [HandleInbound] is only called with a message from a + // peer that is in the connected state. + // + // It is expected that the implementation of this interface can handle + // concurrent calls to [Connected], [Disconnected], and [HandleInbound]. + router router.ExternalHandler } // NewNetwork returns a new Network implementation with the provided parameters. @@ -267,34 +147,10 @@ func NewNetwork( metricsRegisterer prometheus.Registerer, log logging.Logger, listener net.Listener, - router router.Router, + dialer dialer.Dialer, + router router.ExternalHandler, benchlistManager benchlist.Manager, ) (Network, error) { - // #nosec G404 - netw := &network{ - log: log, - currentIP: config.MyIP, - parser: version.NewDefaultApplicationParser(), - listener: listener, - router: router, - dummyNodeID: rand.Uint32(), - disconnectedIPs: make(map[string]struct{}), - connectedIPs: make(map[string]struct{}), - peerAliasIPs: make(map[string]struct{}), - retryDelay: make(map[string]time.Duration), - myIPs: map[string]struct{}{config.MyIP.IP().String(): {}}, - inboundConnUpgradeThrottler: throttling.NewInboundConnUpgradeThrottler(log, config.ThrottlerConfig.InboundConnUpgradeThrottlerConfig), - benchlistManager: benchlistManager, - latestPeerIP: make(map[ids.ShortID]signedPeerIP), - versionCompatibility: version.GetCompatibility(config.NetworkID), - config: config, - mc: msgCreator, - } - - netw.serverUpgrader = NewTLSServerUpgrader(config.TLSConfig) - netw.clientUpgrader = NewTLSClientUpgrader(config.TLSConfig) - - netw.dialer = dialer.NewDialer(constants.NetworkType, config.DialerConfig, log) primaryNetworkValidators, ok := config.Validators.GetValidators(constants.PrimaryNetworkID) if !ok { return nil, errNoPrimaryValidators @@ -310,7 +166,6 @@ func NewNetwork( if err != nil { return nil, fmt.Errorf("initializing inbound message throttler failed with: %w", err) } - netw.inboundMsgThrottler = inboundMsgThrottler outboundMsgThrottler, err := throttling.NewSybilOutboundMsgThrottler( log, @@ -322,24 +177,72 @@ func NewNetwork( if err != nil { return nil, fmt.Errorf("initializing outbound message throttler failed with: %w", err) } - netw.outboundMsgThrottler = outboundMsgThrottler - netw.peers.initialize() - netw.sendFailRateCalculator = math.NewSyncAverager(math.NewAverager(0, config.MaxSendFailRateHalflife, netw.clock.Time())) - if err := netw.metrics.initialize(config.Namespace, metricsRegisterer); err != nil { - return nil, fmt.Errorf("initializing network failed with: %w", err) + peerMetrics, err := peer.NewMetrics(log, config.Namespace, metricsRegisterer) + if err != nil { + return nil, fmt.Errorf("initializing peer metrics failed with: %w", err) + } + + metrics, err := newMetrics(config.Namespace, metricsRegisterer) + if err != nil { + return nil, fmt.Errorf("initializing network metrics failed with: %w", err) + } + + peerConfig := &peer.Config{ + Metrics: peerMetrics, + MessageCreator: msgCreator, + Log: log, + InboundMsgThrottler: inboundMsgThrottler, + OutboundMsgThrottler: outboundMsgThrottler, + Network: nil, // This is set below. + Router: router, + VersionCompatibility: version.GetCompatibility(config.NetworkID), + VersionParser: version.NewDefaultApplicationParser(), + MySubnets: config.WhitelistedSubnets, + Beacons: config.Beacons, + NetworkID: config.NetworkID, + PingFrequency: config.PingFrequency, + PongTimeout: config.PingPongTimeout, + MaxClockDifference: config.MaxClockDifference, + } + n := &network{ + config: config, + peerConfig: peerConfig, + metrics: metrics, + ipSigner: newIPSigner(&config.MyIP, &peerConfig.Clock, config.TLSKey), + + inboundConnUpgradeThrottler: throttling.NewInboundConnUpgradeThrottler(log, config.ThrottlerConfig.InboundConnUpgradeThrottlerConfig), + listener: listener, + dialer: dialer, + serverUpgrader: peer.NewTLSServerUpgrader(config.TLSConfig), + clientUpgrader: peer.NewTLSClientUpgrader(config.TLSConfig), + + onClose: make(chan struct{}), + + sendFailRateCalculator: math.NewSyncAverager(math.NewAverager( + 0, + config.SendFailRateHalflife, + time.Now(), + )), + + trackedIPs: make(map[ids.ShortID]*trackedIP), + connectingPeers: peer.NewSet(), + connectedPeers: peer.NewSet(), + router: router, } - return netw, nil + n.peerConfig.Network = n + return n, nil } -// Assumes [n.stateLock] is not held. func (n *network) Send(msg message.OutboundMessage, nodeIDs ids.ShortSet, subnetID ids.ID, validatorOnly bool) ids.ShortSet { - // retrieve target peers peers := n.getPeers(nodeIDs, subnetID, validatorOnly) - return n.send(msg, peers, true) + n.peerConfig.Metrics.MultipleSendsFailed( + msg.Op(), + nodeIDs.Len()-len(peers), + ) + return n.send(msg, peers) } -// Assumes [n.stateLock] is not held. func (n *network) Gossip( msg message.OutboundMessage, subnetID ids.ID, @@ -347,173 +250,211 @@ func (n *network) Gossip( numValidatorsToSend int, numNonValidatorsToSend int, ) ids.ShortSet { - peers, err := n.selectPeersForGossip(subnetID, validatorOnly, numValidatorsToSend, numNonValidatorsToSend) - if err != nil { - n.log.Error("failed to sample peers: %s", err) + peers := n.samplePeers(subnetID, validatorOnly, numValidatorsToSend, numNonValidatorsToSend) + return n.send(msg, peers) +} - // Because we failed to sample the peers, the message will never be - // sent. This means that we should return the bytes allocated for the - // message. - msg.DecRef() - return nil +// HealthCheck returns information about several network layer health checks. +// 1) Information about health check results +// 2) An error if the health check reports unhealthy +func (n *network) HealthCheck() (interface{}, error) { + n.peersLock.RLock() + connectedTo := n.connectedPeers.Len() + n.peersLock.RUnlock() + + sendFailRate := n.sendFailRateCalculator.Read() + + // Make sure we're connected to at least the minimum number of peers + isConnected := connectedTo >= int(n.config.HealthConfig.MinConnectedPeers) + healthy := isConnected + details := map[string]interface{}{ + ConnectedPeersKey: connectedTo, + } + + // Make sure we've received an incoming message within the threshold + now := n.peerConfig.Clock.Time() + + lastMsgReceivedAt := time.Unix(atomic.LoadInt64(&n.peerConfig.LastReceived), 0) + timeSinceLastMsgReceived := now.Sub(lastMsgReceivedAt) + wasMsgReceivedRecently := timeSinceLastMsgReceived <= n.config.HealthConfig.MaxTimeSinceMsgReceived + healthy = healthy && wasMsgReceivedRecently + details[TimeSinceLastMsgReceivedKey] = timeSinceLastMsgReceived.String() + n.metrics.timeSinceLastMsgReceived.Set(float64(timeSinceLastMsgReceived)) + + // Make sure we've sent an outgoing message within the threshold + lastMsgSentAt := time.Unix(atomic.LoadInt64(&n.peerConfig.LastSent), 0) + timeSinceLastMsgSent := now.Sub(lastMsgSentAt) + wasMsgSentRecently := timeSinceLastMsgSent <= n.config.HealthConfig.MaxTimeSinceMsgSent + healthy = healthy && wasMsgSentRecently + details[TimeSinceLastMsgSentKey] = timeSinceLastMsgSent.String() + n.metrics.timeSinceLastMsgSent.Set(float64(timeSinceLastMsgSent)) + + // Make sure the message send failed rate isn't too high + isMsgFailRate := sendFailRate <= n.config.HealthConfig.MaxSendFailRate + healthy = healthy && isMsgFailRate + details[SendFailRateKey] = sendFailRate + n.metrics.sendFailRate.Set(sendFailRate) + + // Network layer is unhealthy + if !healthy { + var errorReasons []string + if !isConnected { + errorReasons = append(errorReasons, fmt.Sprintf("not connected to a minimum of %d peer(s) only %d", n.config.HealthConfig.MinConnectedPeers, connectedTo)) + } + if !wasMsgReceivedRecently { + errorReasons = append(errorReasons, fmt.Sprintf("no messages from network received in %s > %s", timeSinceLastMsgReceived, n.config.HealthConfig.MaxTimeSinceMsgReceived)) + } + if !wasMsgSentRecently { + errorReasons = append(errorReasons, fmt.Sprintf("no messages from network sent in %s > %s", timeSinceLastMsgSent, n.config.HealthConfig.MaxTimeSinceMsgSent)) + } + if !isMsgFailRate { + errorReasons = append(errorReasons, fmt.Sprintf("messages failure send rate %g > %g", sendFailRate, n.config.HealthConfig.MaxSendFailRate)) + } + + return details, fmt.Errorf("network layer is unhealthy reason: %s", strings.Join(errorReasons, ", ")) } - return n.send(msg, peers, true) + return details, nil } -// Select peers to gossip to. -func (n *network) selectPeersForGossip(subnetID ids.ID, validatorOnly bool, numValidatorsToSample, numNonValidatorsToSample int) ([]*peer, error) { - n.stateLock.RLock() - // Gossip the message to numNonValidatorsToSample random nodes in the - // network. If this is a validator only subnet, selects only validators. - peersAll, err := n.peers.sample(subnetID, validatorOnly, numNonValidatorsToSample) - if err != nil { - n.log.Debug("failed to sample %d peers: %s", numNonValidatorsToSample, err) - n.stateLock.RUnlock() - return nil, err +// Connected is called after the peer finishes the handshake. +// Will not be called after [Disconnected] is called with this peer. +func (n *network) Connected(nodeID ids.ShortID) { + n.peersLock.Lock() + peer, ok := n.connectingPeers.GetByID(nodeID) + if !ok { + n.peerConfig.Log.Error( + "unexpectedly connected to %s%s when not marked as attempting to connect", + constants.NodeIDPrefix, nodeID, + ) + n.peersLock.Unlock() + return } - // Gossip the message to numValidatorsToSample random validators in the - // network. This does not gossip by stake - but uniformly to the validator - // set. - peersValidators, err := n.peers.sample(subnetID, true, numValidatorsToSample) - n.stateLock.RUnlock() - if err != nil { - n.log.Debug("failed to sample %d validators: %s", numValidatorsToSample, err) - return nil, err + tracked, ok := n.trackedIPs[nodeID] + if ok { + tracked.stopTracking() + delete(n.trackedIPs, nodeID) } - peersAll = append(peersAll, peersValidators...) - return peersAll, nil -} + n.connectingPeers.Remove(nodeID) + n.connectedPeers.Add(peer) + n.peersLock.Unlock() -// Send the message to the provided peers. -// -// Send takes ownership of the provided message reference. So, the provided -// message should only be inspected if the reference has been externally -// increased. -// -// Assumes stateLock is not held. -func (n *network) send(msg message.OutboundMessage, peers []*peer, connectedOnly bool) ids.ShortSet { - var ( - now = n.clock.Time() - msgLen = len(msg.Bytes()) - sentTo = ids.NewShortSet(len(peers)) - op = msg.Op() - ) + n.metrics.numPeers.Inc() + n.metrics.connected.Inc() + + peerVersion := peer.Version() + n.router.Connected(nodeID, peerVersion) +} - msgMetrics := n.metrics.messageMetrics[op] - if msgMetrics == nil { - n.log.Error("unregistered metric for message with op %s. Dropping it", op) +// AllowConnection returns true if this node should have a connection to the +// provided nodeID. If the node is attempting to connect to the minimum number +// of peers, then it should only connect if this node is a validator, or the +// peer is a validator/beacon. +func (n *network) AllowConnection(nodeID ids.ShortID) bool { + return !n.config.RequireValidatorToConnect || + n.config.Validators.Contains(constants.PrimaryNetworkID, n.config.MyNodeID) || + n.WantsConnection(nodeID) +} - // The message wasn't passed to any peers, so we can remove the - // reference that was added. - msg.DecRef() - return sentTo +func (n *network) Track(ip utils.IPCertDesc) { + nodeID := peer.CertToID(ip.Cert) + if !n.config.AllowPrivateIPs && ip.IPDesc.IsPrivate() { + n.peerConfig.Log.Verbo( + "dropping suggested connected to %s%s because the ip (%s) is private", + constants.NodeIDPrefix, nodeID, + ip.IPDesc, + ) + return } - // send to peer and update metrics - // note: peer may be nil - for _, peer := range peers { - // Add a reference to the message so that if it is sent, it won't be - // collected until it is done being processed. - msg.AddRef() - if peer != nil && - (!connectedOnly || peer.finishedHandshake.GetValue()) && - !sentTo.Contains(peer.nodeID) && - peer.Send(msg) { - sentTo.Add(peer.nodeID) + n.peersLock.Lock() + defer n.peersLock.Unlock() - // record metrics for success - n.sendFailRateCalculator.Observe(0, now) - msgMetrics.numSent.Inc() - msgMetrics.sentBytes.Add(float64(msgLen)) - if saved := msg.BytesSavedCompression(); saved != 0 { - msgMetrics.savedSentBytes.Observe(float64(saved)) - } - } else { - // record metrics for failure - n.sendFailRateCalculator.Observe(1, now) - msgMetrics.numFailed.Inc() + _, connected := n.connectedPeers.GetByID(nodeID) + if connected { + // If I'm currently connected to [nodeID] then they will have told me + // how to connect to them in the future, and I don't need to attempt to + // connect to them now. + return + } - // The message wasn't passed to the peer, so we should remove the - // reference that was added. - msg.DecRef() + tracked, isTracked := n.trackedIPs[nodeID] + if isTracked { + if tracked.ip.Timestamp < ip.Time { + // Stop tracking the old IP and instead start tracking new one. + tracked := tracked.trackNewIP(&peer.UnsignedIP{ + IP: ip.IPDesc, + Timestamp: ip.Time, + }) + n.trackedIPs[nodeID] = tracked + n.dial(nodeID, tracked) } + } else if n.WantsConnection(nodeID) { + tracked := newTrackedIP(&peer.UnsignedIP{ + IP: ip.IPDesc, + Timestamp: ip.Time, + }) + n.trackedIPs[nodeID] = tracked + n.dial(nodeID, tracked) } - - // The message has been passed to all peers that it will be sent to, so we - // can decrease the sender reference now. - msg.DecRef() - return sentTo } -// shouldUpgradeIncoming returns whether we should upgrade an incoming -// connection from a peer at the IP whose string repr. is [ipStr]. -// Assumes stateLock is not held. -func (n *network) shouldUpgradeIncoming(ip utils.IPDesc) bool { - n.stateLock.RLock() - defer n.stateLock.RUnlock() - - ipStr := ip.String() - if _, ok := n.connectedIPs[ipStr]; ok { - n.log.Debug("not upgrading connection to %s because it's connected", ipStr) - return false +// Disconnected is called after the peer's handling has been shutdown. +// It is not guaranteed that [Connected] was previously called with [nodeID]. +// It is guaranteed that [Connected] will not be called with [nodeID] after this +// call. Note that this is from the perspective of a single peer object, because +// a peer with the same ID can reconnect to this network instance. +func (n *network) Disconnected(nodeID ids.ShortID) { + n.peersLock.RLock() + _, connecting := n.connectingPeers.GetByID(nodeID) + peer, connected := n.connectedPeers.GetByID(nodeID) + n.peersLock.RUnlock() + + if connecting { + n.disconnectedFromConnecting(nodeID) } - if _, ok := n.myIPs[ipStr]; ok { - n.log.Debug("not upgrading connection to %s because it's myself", ipStr) - return false + if connected { + n.disconnectedFromConnected(peer, nodeID) } - if _, ok := n.peerAliasIPs[ipStr]; ok { - n.log.Debug("not upgrading connection to %s because it's an alias", ipStr) - return false - } - if !n.inboundConnUpgradeThrottler.ShouldUpgrade(ip) { - n.log.Debug("not upgrading connection to %s due to rate-limiting", ipStr) - n.metrics.inboundConnRateLimited.Inc() - return false +} + +func (n *network) Version() (message.OutboundMessage, error) { + mySignedIP, err := n.ipSigner.getSignedIP() + if err != nil { + return nil, err } - n.metrics.inboundConnAllowed.Inc() + return n.peerConfig.MessageCreator.Version( + n.peerConfig.NetworkID, + n.peerConfig.Clock.Unix(), + mySignedIP.IP.IP, + n.peerConfig.VersionCompatibility.Version().String(), + mySignedIP.IP.Timestamp, + mySignedIP.Signature, + n.peerConfig.MySubnets.List(), + ) +} - // Note that we attempt to upgrade remote addresses in - // [n.disconnectedIPs] because that could allow us to initialize - // a connection with a peer we've been attempting to dial. - return true +func (n *network) Peers() (message.OutboundMessage, error) { + peers := n.sampleValidatorIPs() + return n.peerConfig.MessageCreator.PeerList(peers, true) } -// shouldHoldConnection returns true if this node should have a connection to -// the provided peerID. If the node is attempting to connect to the minimum -// number of peers, then it should only connect if this node is a validator, or -// the peer is a validator/beacon. -func (n *network) shouldHoldConnection(peerID ids.ShortID) bool { - return !n.config.RequireValidatorToConnect || - n.config.Validators.Contains(constants.PrimaryNetworkID, n.config.MyNodeID) || - n.config.Validators.Contains(constants.PrimaryNetworkID, peerID) || - n.config.Beacons.Contains(peerID) +func (n *network) Pong(nodeID ids.ShortID) (message.OutboundMessage, error) { + uptimePercentFloat, err := n.config.UptimeCalculator.CalculateUptimePercent(nodeID) + if err != nil { + uptimePercentFloat = 0 + } + + uptimePercentInt := uint8(uptimePercentFloat * 100) + return n.peerConfig.MessageCreator.Pong(uptimePercentInt) } // Dispatch starts accepting connections from other nodes attempting to connect // to this node. -// Assumes [n.stateLock] is not held. func (n *network) Dispatch() error { - go n.gossipPeerList() // Periodically gossip peers - go n.updateUptimeMetrics() // Periodically update uptime metrics + go n.runTimers() // Periodically perform operations go n.inboundConnUpgradeThrottler.Dispatch() - defer n.inboundConnUpgradeThrottler.Stop() - go func() { - duration := time.Until(n.versionCompatibility.MaskTime()) - time.Sleep(duration) - - n.stateLock.Lock() - defer n.stateLock.Unlock() - - n.hasMasked = true - for _, vdrID := range n.maskedValidators.List() { - if err := n.config.Validators.MaskValidator(vdrID); err != nil { - n.log.Error("failed to mask validator %s due to %s", vdrID, err) - } - } - n.maskedValidators.Clear() - n.log.Verbo("The new staking set is:\n%s", n.config.Validators) - }() + errs := wrappers.Errs{} for { // Continuously accept new connections conn, err := n.listener.Accept() // Returns error when n.Close() is called if err != nil { @@ -524,14 +465,8 @@ func (n *network) Dispatch() error { continue } - // When [n].Close() is called, [n.listener].Close() is called. - // This causes [n.listener].Accept() to return an error. - // If that happened, don't log/return an error here. - if n.closed.GetValue() { - return errNetworkClosed - } - n.log.Debug("error during server accept: %s", err) - return err + n.peerConfig.Log.Debug("error during server accept: %s", err) + break } // We pessimistically drop an incoming connection if the remote @@ -544,761 +479,556 @@ func (n *network) Dispatch() error { remoteAddr := conn.RemoteAddr().String() ip, err := utils.ToIPDesc(remoteAddr) if err != nil { - return fmt.Errorf("unable to convert remote address %s to IPDesc: %w", remoteAddr, err) + errs.Add(fmt.Errorf("unable to convert remote address %s to IPDesc: %w", remoteAddr, err)) + break } - upgrade := n.shouldUpgradeIncoming(ip) - if !upgrade { + + if !n.inboundConnUpgradeThrottler.ShouldUpgrade(ip) { + n.peerConfig.Log.Debug( + "not upgrading connection to %s due to rate-limiting", + ip, + ) + n.metrics.inboundConnRateLimited.Inc() _ = conn.Close() continue } - - if conn, ok := conn.(*net.TCPConn); ok { - if err := conn.SetLinger(0); err != nil { - n.log.Warn("failed to set no linger due to: %s", err) - } - if err := conn.SetNoDelay(true); err != nil { - n.log.Warn("failed to set socket nodelay due to: %s", err) - } - } + n.metrics.inboundConnAllowed.Inc() go func() { - if err := n.upgrade(newPeer(n, conn, utils.IPDesc{}), n.serverUpgrader); err != nil { - n.log.Verbo("failed to upgrade connection: %s", err) + if err := n.upgrade(conn, n.serverUpgrader); err != nil { + n.peerConfig.Log.Verbo("failed to upgrade inbound connection: %s", err) } }() } -} - -// Returns information about peers. -// If [nodeIDs] is empty, returns info about all peers that have finished -// the handshake. Otherwise, returns info about the peers in [nodeIDs] -// that have finished the handshake. -// Assumes [n.stateLock] is not held. -func (n *network) Peers(nodeIDs []ids.ShortID) []PeerInfo { - n.stateLock.RLock() - defer n.stateLock.RUnlock() - - if len(nodeIDs) == 0 { // Return info about all peers - peers := make([]PeerInfo, 0, n.peers.size()) - for _, peer := range n.peers.peersList { - if peer.finishedHandshake.GetValue() { - peers = append(peers, n.NewPeerInfo(peer)) - } - } - return peers - } + n.inboundConnUpgradeThrottler.Stop() + n.StartClose() - peers := make([]PeerInfo, 0, len(nodeIDs)) - for _, nodeID := range nodeIDs { // Return info about given peers - if peer, ok := n.peers.getByID(nodeID); ok && peer.finishedHandshake.GetValue() { - peers = append(peers, n.NewPeerInfo(peer)) - } - } - return peers -} + n.peersLock.RLock() + connecting := n.connectingPeers.Sample(n.connectingPeers.Len(), peer.NoPrecondition) + connected := n.connectedPeers.Sample(n.connectedPeers.Len(), peer.NoPrecondition) + n.peersLock.RUnlock() -func (n *network) NewPeerInfo(peer *peer) PeerInfo { - publicIPStr := "" - if !peer.ip.IsZero() { - publicIPStr = peer.getIP().String() - } - return PeerInfo{ - IP: peer.conn.RemoteAddr().String(), - PublicIP: publicIPStr, - ID: peer.nodeID.PrefixedString(constants.NodeIDPrefix), - Version: peer.versionStr.GetValue().(string), - LastSent: time.Unix(atomic.LoadInt64(&peer.lastSent), 0), - LastReceived: time.Unix(atomic.LoadInt64(&peer.lastReceived), 0), - Benched: n.benchlistManager.GetBenched(peer.nodeID), - ObservedUptime: json.Uint8(peer.observedUptime), - TrackedSubnets: peer.trackedSubnets.List(), + for _, peer := range append(connecting, connected...) { + errs.Add(peer.AwaitClosed(context.TODO())) } + return errs.Err } -// Assumes [n.stateLock] is not held. -func (n *network) Close() error { - n.closeOnce.Do(n.close) - return nil +func (n *network) WantsConnection(nodeID ids.ShortID) bool { + return n.config.Validators.Contains(constants.PrimaryNetworkID, nodeID) || + n.config.Beacons.Contains(nodeID) } -func (n *network) close() { - n.log.Info("shutting down network") - - if err := n.listener.Close(); err != nil { - n.log.Debug("closing network listener failed with: %s", err) - } - - if n.closed.GetValue() { - return - } +func (n *network) ManuallyTrack(nodeID ids.ShortID, ip utils.IPDesc) { + n.peersLock.Lock() + defer n.peersLock.Unlock() - n.stateLock.Lock() - if n.closed.GetValue() { - n.stateLock.Unlock() + _, connected := n.connectedPeers.GetByID(nodeID) + if connected { + // If I'm currently connected to [nodeID] then they will have told me + // how to connect to them in the future, and I don't need to attempt to + // connect to them now. return } - n.closed.SetValue(true) - peersToClose := make([]*peer, n.peers.size()) - copy(peersToClose, n.peers.peersList) - n.peers.reset() - n.stateLock.Unlock() - - for _, peer := range peersToClose { - peer.Close() // Grabs the stateLock + _, isTracked := n.trackedIPs[nodeID] + if !isTracked { + tracked := newTrackedIP(&peer.UnsignedIP{ + IP: ip, + Timestamp: 0, + }) + n.trackedIPs[nodeID] = tracked + n.dial(nodeID, tracked) } } -// Assumes [n.stateLock] is not held. -func (n *network) TrackIP(ip utils.IPDesc) { - n.Track(ip, ids.ShortEmpty) -} - -// Assumes [n.stateLock] is not held. -func (n *network) Track(ip utils.IPDesc, nodeID ids.ShortID) { - n.stateLock.Lock() - defer n.stateLock.Unlock() - - n.track(ip, nodeID) -} - -func (n *network) IP() utils.IPDesc { - return n.currentIP.IP() +func (n *network) sampleValidatorIPs() []utils.IPCertDesc { + n.peersLock.RLock() + peers := n.connectedPeers.Sample( + int(n.config.PeerListNumValidatorIPs), + func(p peer.Peer) bool { + // Only sample validators + return n.config.Validators.Contains(constants.PrimaryNetworkID, p.ID()) + }, + ) + n.peersLock.RUnlock() + + sampledIPs := make([]utils.IPCertDesc, len(peers)) + for i, peer := range peers { + ip := peer.IP() + sampledIPs[i] = utils.IPCertDesc{ + Cert: peer.Cert(), + IPDesc: ip.IP.IP, + Time: ip.IP.Timestamp, + Signature: ip.Signature, + } + } + return sampledIPs } -func (n *network) NodeUptime() (UptimeResult, bool) { - n.stateLock.RLock() - defer n.stateLock.RUnlock() - primaryValidators, ok := n.config.Validators.GetValidators(constants.PrimaryNetworkID) - if !ok { - return UptimeResult{}, false - } +// getPeers returns a slice of connected peers from a set of [nodeIDs]. +// +// - [nodeIDs] the IDs of the peers that should be returned if they are +// connected. +// - [subnetID] the subnetID whose membership should be considered if +// [validatorOnly] is set to true. +// - [validatorOnly] is the flag to drop any nodes from [nodeIDs] that are not +// validators in [subnetID]. +func (n *network) getPeers( + nodeIDs ids.ShortSet, + subnetID ids.ID, + validatorOnly bool, +) []peer.Peer { + peers := make([]peer.Peer, 0, nodeIDs.Len()) - myStake, isValidator := primaryValidators.GetWeight(n.config.MyNodeID) - if !isValidator { - return UptimeResult{}, false - } + n.peersLock.RLock() + defer n.peersLock.RUnlock() - var ( - totalWeight = float64(primaryValidators.Weight()) - totalWeightedPercent = 100 * float64(myStake) - rewardingStake = float64(myStake) - ) - for _, peer := range n.peers.peersList { - weight, ok := primaryValidators.GetWeight(peer.nodeID) + for nodeID := range nodeIDs { + peer, ok := n.connectedPeers.GetByID(nodeID) if !ok { - // this is not a validator skip it. continue } - if !peer.finishedHandshake.GetValue() { + trackedSubnets := peer.TrackedSubnets() + if !trackedSubnets.Contains(subnetID) { continue } - percent := float64(peer.observedUptime) - weightFloat := float64(weight) - totalWeightedPercent += percent * weightFloat - - // if this peer thinks we're above requirement add the weight - if percent/100 >= n.config.UptimeRequirement { - rewardingStake += weightFloat + if validatorOnly && !n.config.Validators.Contains(subnetID, nodeID) { + continue } + + peers = append(peers, peer) } - return UptimeResult{ - WeightedAveragePercentage: gomath.Abs(totalWeightedPercent / totalWeight), - RewardingStakePercentage: gomath.Abs(100 * rewardingStake / totalWeight), - }, true + return peers } -// assumes the stateLock is held. -// Try to connect to [nodeID] at [ip]. -func (n *network) track(ip utils.IPDesc, nodeID ids.ShortID) { - if n.closed.GetValue() { - return - } - - str := ip.String() - if _, ok := n.disconnectedIPs[str]; ok { - return - } - if _, ok := n.connectedIPs[str]; ok { - return - } - if _, ok := n.peerAliasIPs[str]; ok { - return - } - if _, ok := n.myIPs[str]; ok { - return - } - // If we saw an IP gossiped for this node ID - // with a later timestamp, don't track this old IP - if latestIP, ok := n.latestPeerIP[nodeID]; ok { - if !latestIP.ip.Equal(ip) { - return - } +func (n *network) samplePeers( + subnetID ids.ID, + validatorOnly bool, + numValidatorsToSample, + numNonValidatorsToSample int, +) []peer.Peer { + if validatorOnly { + numValidatorsToSample += numNonValidatorsToSample + numNonValidatorsToSample = 0 } - n.disconnectedIPs[str] = struct{}{} - go n.connectTo(ip, nodeID) -} + n.peersLock.RLock() + defer n.peersLock.RUnlock() -// Assumes [n.stateLock] is not held. Only returns after the network is closed. -func (n *network) gossipPeerList() { - t := time.NewTicker(n.config.PeerListGossipFreq) - defer t.Stop() + return n.connectedPeers.Sample( + numValidatorsToSample+numNonValidatorsToSample, + func(p peer.Peer) bool { + if n.config.Validators.Contains(subnetID, p.ID()) { + numValidatorsToSample-- + return numValidatorsToSample >= 0 + } - for range t.C { - if n.closed.GetValue() { - return - } + numNonValidatorsToSample-- + return numNonValidatorsToSample >= 0 + }, + ) +} - ipCerts, err := n.validatorIPs() - if err != nil { - n.log.Error("failed to fetch validator IPs: %s", err) - continue - } +// send the message to the provided peers. +// +// send takes ownership of the provided message reference. So, the provided +// message should only be inspected if the reference has been externally +// increased. +func (n *network) send(msg message.OutboundMessage, peers []peer.Peer) ids.ShortSet { + sentTo := ids.NewShortSet(len(peers)) + now := n.peerConfig.Clock.Time() - if len(ipCerts) == 0 { - n.log.Debug("skipping validator IP gossiping as no IPs are connected") - continue - } + // send to peer and update metrics + for _, peer := range peers { + // Add a reference to the message so that if it is sent, it won't be + // collected until it is done being processed. + msg.AddRef() + if peer.Send(msg) { + sentTo.Add(peer.ID()) - msg, err := n.mc.PeerList(ipCerts) - if err != nil { - n.log.Error("failed to build signed peerlist to gossip: %s. len(ips): %d", - err, - len(ipCerts)) - continue + // TODO: move send fail rate calculations into the peer metrics + // record metrics for success + n.sendFailRateCalculator.Observe(0, now) + } else { + // record metrics for failure + n.sendFailRateCalculator.Observe(1, now) } - - numStakersToSend := int((n.config.PeerListGossipSize + n.config.PeerListStakerGossipFraction - 1) / n.config.PeerListStakerGossipFraction) - numNonStakersToSend := int(n.config.PeerListGossipSize) - numStakersToSend - - n.Gossip(msg, constants.PrimaryNetworkID, false, numStakersToSend, numNonStakersToSend) } + + // The message has been passed to all peers that it will be sent to, so we + // can decrease the sender reference now. + msg.DecRef() + return sentTo } -// Assumes [n.stateLock] is not held. Only returns after the network is closed. -func (n *network) updateUptimeMetrics() { - t := time.NewTicker(n.config.UptimeMetricFreq) - defer t.Stop() +func (n *network) disconnectedFromConnecting(nodeID ids.ShortID) { + n.peersLock.Lock() + defer n.peersLock.Unlock() - for range t.C { - if n.closed.GetValue() { - return + n.connectingPeers.Remove(nodeID) + + // The peer that is disconnecting from us didn't finish the handshake + tracked, ok := n.trackedIPs[nodeID] + if ok { + if n.WantsConnection(nodeID) { + tracked := tracked.trackNewIP(tracked.ip) + n.trackedIPs[nodeID] = tracked + n.dial(nodeID, tracked) + } else { + tracked.stopTracking() + delete(n.trackedIPs, nodeID) } - result, _ := n.NodeUptime() - n.metrics.nodeUptimeWeightedAverage.Set(result.WeightedAveragePercentage) - n.metrics.nodeUptimeRewardingStake.Set(result.RewardingStakePercentage) } -} - -// Returns when: -// * We connected to [ip] -// * The network is closed -// * We gave up connecting to [ip] because the IP is stale -// If [nodeID] == ids.ShortEmpty, won't cancel an existing -// attempt to connect to the peer with that IP. -// We do this so we don't cancel attempted to connect to bootstrap beacons. -// See method TrackIP. -// Assumes [n.stateLock] isn't held when this method is called. -func (n *network) connectTo(ip utils.IPDesc, nodeID ids.ShortID) { - str := ip.String() - n.stateLock.RLock() - delay := n.retryDelay[str] - n.stateLock.RUnlock() - for { - time.Sleep(delay) + n.metrics.disconnected.Inc() +} - if delay == 0 { - delay = n.config.InitialReconnectDelay - } +func (n *network) disconnectedFromConnected(peer peer.Peer, nodeID ids.ShortID) { + n.router.Disconnected(nodeID) - // Randomization is only performed here to distribute reconnection - // attempts to a node that previously shut down. This doesn't require - // cryptographically secure random number generation. - delay = time.Duration(float64(delay) * (1 + rand.Float64())) // #nosec G404 - if delay > n.config.MaxReconnectDelay { - // set the timeout to [.75, 1) * maxReconnectDelay - delay = time.Duration(float64(n.config.MaxReconnectDelay) * (3 + rand.Float64()) / 4) // #nosec G404 - } + n.peersLock.Lock() + defer n.peersLock.Unlock() - n.stateLock.Lock() - _, isDisconnected := n.disconnectedIPs[str] - _, isConnected := n.connectedIPs[str] - _, isMyself := n.myIPs[str] - // If we saw an IP gossiped for this node ID - // with a later timestamp, don't track this old IP - isLatestIP := true - if latestIP, ok := n.latestPeerIP[nodeID]; ok { - isLatestIP = latestIP.ip.Equal(ip) - } - closed := n.closed + n.connectedPeers.Remove(nodeID) - if !isDisconnected || !isLatestIP || isConnected || isMyself || closed.GetValue() { - // If the IP was discovered by the peer connecting to us, we don't - // need to attempt to connect anymore + // The peer that is disconnecting from us finished the handshake + if n.WantsConnection(nodeID) { + tracked := newTrackedIP(&peer.IP().IP) + n.trackedIPs[nodeID] = tracked + n.dial(nodeID, tracked) + } else { + delete(n.trackedIPs, nodeID) + } - // If we've seen an IP gossiped for this peer with a later timestamp, - // don't try to connect to the old IP anymore + n.metrics.numPeers.Dec() + n.metrics.disconnected.Inc() +} - // If the IP was discovered to be our IP address, we don't need to - // attempt to connect anymore +// dial will spin up a new goroutine and attempt to establish a connection with +// [nodeID] at [ip]. +// +// If the connection established at [ip] doesn't match [nodeID]: +// - attempts to reach [nodeID] at [ip] will be halted. +// - the connection will be checked to see if the connection is desired or not. +// +// If [ip] has been flagged with [ip.stopTracking] then this goroutine will +// exit. +// +// If [nodeID] is marked as connecting or connected then this goroutine will +// exit. +// +// If [nodeID] is no longer marked as desired then this goroutine will exit and +// the entry in the [trackedIP]s set will be removed. +// +// If initiating a connection to [ip] fails, then dial will reattempt. However, +// there is a randomized exponential backoff to avoid spamming connection +// attempts. +func (n *network) dial(nodeID ids.ShortID, ip *trackedIP) { + go func() { + n.metrics.numTracked.Inc() + defer n.metrics.numTracked.Dec() - // If the network was closed, we should stop attempting to connect - // to the peer + for { + timer := time.NewTimer(ip.getDelay()) - n.stateLock.Unlock() - return - } - n.retryDelay[str] = delay - n.stateLock.Unlock() - - // If we are already trying to connect to this node ID, - // cancel the existing attempt. - // If [nodeID] is the empty ID, [ip] is a bootstrap beacon. - // In that case, don't cancel existing connection attempt. - if nodeID != ids.ShortEmpty { - if cancel, exists := n.connAttempts.Load(nodeID); exists { - n.log.Verbo("canceling attempt to connect to stale IP of %s%s", constants.NodeIDPrefix, nodeID) - cancel.(context.CancelFunc)() + select { + case <-ip.onStopTracking: + timer.Stop() + return + case <-timer.C: } - } - // When [cancel] is called, we give up on this attempt to connect - // (if we have not yet connected.) - // This occurs at the sooner of: - // * [connectTo] is called again with the same node ID - // * The call to [attemptConnect] below returns - ctx, cancel := context.WithCancel(context.Background()) - n.connAttempts.Store(nodeID, cancel) - - // Attempt to connect - err := n.attemptConnect(ctx, ip) - cancel() // to avoid goroutine leak + n.peersLock.Lock() + if !n.WantsConnection(nodeID) { + // Typically [n.trackedIPs[nodeID]] will already equal [ip], but + // the reference to [ip] is refreshed to avoid any potential + // race conditions before removing the entry. + if ip, exists := n.trackedIPs[nodeID]; exists { + ip.stopTracking() + delete(n.trackedIPs, nodeID) + } + n.peersLock.Unlock() + return + } + _, connecting := n.connectingPeers.GetByID(nodeID) + _, connected := n.connectedPeers.GetByID(nodeID) + n.peersLock.Unlock() + + // While it may not be strictly needed to stop attempting to connect + // to an already connected peer here. It does prevent unnecessary + // outbound connections. Additionally, because the peer would + // immediately drop a duplicated connection, this prevents any + // "connection reset by peer" errors from interfering with the + // later duplicated connection check. + if connecting || connected { + n.peerConfig.Log.Verbo( + "exiting attempt to dial %s%s as we are already connected", + constants.NodeIDPrefix, nodeID, + ) + return + } - n.connAttempts.Delete(nodeID) + // Increase the delay that we will use for a future connection + // attempt. + ip.increaseDelay( + n.config.InitialReconnectDelay, + n.config.MaxReconnectDelay, + ) + + // TODO: specify dial timeout + conn, err := n.dialer.Dial(context.TODO(), ip.ip.IP) + if err != nil { + n.peerConfig.Log.Verbo( + "failed to reach %s, attempting again in %s", + ip.ip, + ip.delay, + ) + continue + } - if err == nil { + err = n.upgrade(conn, n.clientUpgrader) + if err != nil { + n.peerConfig.Log.Verbo( + "failed to upgrade %s, attempting again in %s", + ip.ip, + ip.delay, + ) + continue + } return } - n.log.Verbo("error attempting to connect to %s: %s. Reattempting in %s", - ip, err, delay) - } + }() } -// Attempt to connect to the peer at [ip]. -// If [ctx] is canceled, stops trying to connect. -// Returns nil if: -// * A connection was established -// * The network is closed. -// * [ctx] is canceled. -// Assumes [n.stateLock] is not held when this method is called. -func (n *network) attemptConnect(ctx context.Context, ip utils.IPDesc) error { - n.log.Verbo("attempting to connect to %s", ip) - conn, err := n.dialer.Dial(ctx, ip) - if err != nil { - // If [ctx] was canceled, return nil so we don't try to connect again - if ctx.Err() == context.Canceled { - return nil - } - // Error wasn't because connection attempt was canceled - return err - } - +// upgrade the provided connection, which may be an inbound connection or an +// outbound connection, with the provided [upgrader]. +// +// If the connection is successfully upgraded, [nil] will be returned. +// +// If the connection is desired by the node, then the resulting upgraded +// connection will be used to create a new peer. Otherwise the connection will +// be immediately closed. +func (n *network) upgrade(conn net.Conn, upgrader peer.Upgrader) error { if conn, ok := conn.(*net.TCPConn); ok { + // If a connection is closed, we shouldn't bother keeping any messages + // in memory. if err := conn.SetLinger(0); err != nil { - n.log.Warn("failed to set no linger due to: %s", err) - } - if err := conn.SetNoDelay(true); err != nil { - n.log.Warn("failed to set socket nodelay due to: %s", err) + n.peerConfig.Log.Warn("failed to set no linger due to: %s", err) } } - return n.upgrade(newPeer(n, conn, ip), n.clientUpgrader) -} -// Assumes [n.stateLock] is not held. Returns an error if the peer's connection -// wasn't able to be upgraded. -func (n *network) upgrade(p *peer, upgrader Upgrader) error { - if err := p.conn.SetReadDeadline(time.Now().Add(n.config.ReadHandshakeTimeout)); err != nil { - _ = p.conn.Close() - n.log.Verbo("failed to set the read deadline with %s", err) + upgradeTimeout := n.peerConfig.Clock.Time().Add(n.config.ReadHandshakeTimeout) + if err := conn.SetReadDeadline(upgradeTimeout); err != nil { + _ = conn.Close() + n.peerConfig.Log.Verbo("failed to set the read deadline with %s", err) return err } - nodeID, conn, cert, err := upgrader.Upgrade(p.conn) + nodeID, tlsConn, cert, err := upgrader.Upgrade(conn) if err != nil { - _ = p.conn.Close() - n.log.Verbo("failed to upgrade connection with %s", err) + _ = conn.Close() + n.peerConfig.Log.Verbo("failed to upgrade connection with %s", err) return err } - if err := conn.SetReadDeadline(time.Time{}); err != nil { - _ = p.conn.Close() - n.log.Verbo("failed to clear the read deadline with %s", err) + if err := tlsConn.SetReadDeadline(time.Time{}); err != nil { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo("failed to clear the read deadline with %s", err) return err } - p.cert = cert - p.nodeID = nodeID - p.conn = conn + // At this point we have successfully upgraded the connection and will + // return a nil error. - if err := n.tryAddPeer(p); err != nil { - _ = p.conn.Close() - n.log.Debug("dropping peer connection due to: %s", err) + if nodeID == n.config.MyNodeID { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo("dropping connection to myself") + return nil } - return nil -} -// Returns an error if the peer couldn't be added. -// Assumes [n.stateLock] is not held. -func (n *network) tryAddPeer(p *peer) error { - n.stateLock.Lock() - defer n.stateLock.Unlock() - - if n.closed.GetValue() { - // the network is closing, so make sure that no further reconnect - // attempts are made. - return errNetworkClosed + if !n.AllowConnection(nodeID) { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo( + "dropping undesired connection to %s%s", + constants.NodeIDPrefix, nodeID, + ) + return nil } - ip := p.getIP() - - // if this connection is myself, then I should delete the connection and - // mark the IP as one of mine. - if p.nodeID == n.config.MyNodeID { - if !ip.IsZero() { - // if n.ip is less useful than p.ip set it to this IP - if n.currentIP.IP().IsZero() { - n.log.Info("setting my ip to %s because I was able to connect to myself through this channel", - p.ip) - n.currentIP.Update(p.ip) - } - str := ip.String() - delete(n.disconnectedIPs, str) - delete(n.retryDelay, str) - n.myIPs[str] = struct{}{} - } - return errPeerIsMyself + n.peersLock.Lock() + defer n.peersLock.Unlock() + + if n.closing { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo( + "dropping connection to %s%s because we are shutting down the p2p network", + constants.NodeIDPrefix, nodeID, + ) + return nil } - if !n.shouldHoldConnection(p.nodeID) { - if !ip.IsZero() { - str := ip.String() - delete(n.disconnectedIPs, str) - delete(n.retryDelay, str) - } - return fmt.Errorf("non-validator connection from %s at %s", p.nodeID.PrefixedString(constants.NodeIDPrefix), ip) + if _, connecting := n.connectingPeers.GetByID(nodeID); connecting { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo( + "dropping duplicate connection to %s%s because we are already connecting to it", + constants.NodeIDPrefix, nodeID, + ) + return nil } - // If I am already connected to this peer, then I should close this new - // connection and add an alias record. - if peer, ok := n.peers.getByID(p.nodeID); ok { - if !ip.IsZero() { - str := ip.String() - delete(n.disconnectedIPs, str) - delete(n.retryDelay, str) - peer.addAlias(ip) - } - return fmt.Errorf("duplicated connection from %s at %s", p.nodeID.PrefixedString(constants.NodeIDPrefix), ip) + if _, connected := n.connectedPeers.GetByID(nodeID); connected { + _ = tlsConn.Close() + n.peerConfig.Log.Verbo( + "dropping duplicate connection to %s%s because we are already connected to it", + constants.NodeIDPrefix, nodeID, + ) + return nil } - n.peers.add(p) - n.metrics.numPeers.Set(float64(n.peers.size())) - p.Start() + n.peerConfig.Log.Verbo( + "starting handshake with %s%s", + constants.NodeIDPrefix, nodeID, + ) + + peer := peer.Start(n.peerConfig, tlsConn, cert, nodeID) + n.connectingPeers.Add(peer) return nil } -// Returns the IPs, certs and signatures of validators we're connected -// to that have finished the handshake. -// Assumes [n.stateLock] is not held. -func (n *network) validatorIPs() ([]utils.IPCertDesc, error) { - n.stateLock.RLock() - defer n.stateLock.RUnlock() +func (n *network) PeerInfo(nodeIDs []ids.ShortID) []peer.Info { + n.peersLock.RLock() + defer n.peersLock.RUnlock() - totalNumPeers := n.peers.size() - - numToSend := int(n.config.PeerListSize) - if totalNumPeers < numToSend { - numToSend = totalNumPeers + if len(nodeIDs) == 0 { + return n.connectedPeers.AllInfo() } + return n.connectedPeers.Info(nodeIDs) +} - if numToSend == 0 { - return nil, nil - } - res := make([]utils.IPCertDesc, 0, numToSend) +func (n *network) StartClose() { + n.closeOnce.Do(func() { + n.peerConfig.Log.Info("shutting down the p2p networking") - s := sampler.NewUniform() - if err := s.Initialize(uint64(totalNumPeers)); err != nil { - return nil, err - } - - for len(res) != numToSend { - sampledIdx, err := s.Next() // lazy-sampling - if err != nil { - // all peers have been sampled and not enough valid ones found. - // return what we have - return res, nil + if err := n.listener.Close(); err != nil { + n.peerConfig.Log.Debug("closing the network listener failed with: %s", err) } - // TODO: consider possibility of grouping peers in different buckets - // (e.g. validators/non-validators, connected/disconnected) - peer, found := n.peers.getByIdx(int(sampledIdx)) - if !found { - return res, fmt.Errorf("no peer at index %v", sampledIdx) - } + n.peersLock.Lock() + defer n.peersLock.Unlock() - peerIP := peer.getIP() - switch { - case !peer.finishedHandshake.GetValue(): - continue - case peerIP.IsZero(): - continue - case !n.config.Validators.Contains(constants.PrimaryNetworkID, peer.nodeID): - continue - } + close(n.onClose) + n.closing = true - peerVersion := peer.versionStruct.GetValue().(version.Application) - if n.versionCompatibility.Unmaskable(peerVersion) != nil { - continue + for nodeID, tracked := range n.trackedIPs { + tracked.stopTracking() + delete(n.trackedIPs, nodeID) } - signedIP, ok := peer.sigAndTime.GetValue().(signedPeerIP) - if !ok || !signedIP.ip.Equal(peerIP) { - continue + for i := 0; i < n.connectingPeers.Len(); i++ { + peer, _ := n.connectingPeers.GetByIndex(i) + peer.StartClose() } - res = append(res, utils.IPCertDesc{ - IPDesc: peerIP, - Signature: signedIP.signature, - Cert: peer.cert, - Time: signedIP.time, - }) - } - - return res, nil + for i := 0; i < n.connectedPeers.Len(); i++ { + peer, _ := n.connectedPeers.GetByIndex(i) + peer.StartClose() + } + }) } -// Should only be called after the peer finishes the handshake. -// Should not be called after [disconnected] is called with this peer. -// Assumes [n.stateLock] is not held. -func (n *network) connected(p *peer) { - p.net.stateLock.Lock() - defer p.net.stateLock.Unlock() - - p.finishedHandshake.SetValue(true) - - peerVersion := p.versionStruct.GetValue().(version.Application) - - if n.hasMasked { - if n.versionCompatibility.Unmaskable(peerVersion) != nil { - if err := n.config.Validators.MaskValidator(p.nodeID); err != nil { - n.log.Error("failed to mask validator %s due to %s", p.nodeID, err) - } - } else { - if err := n.config.Validators.RevealValidator(p.nodeID); err != nil { - n.log.Error("failed to reveal validator %s due to %s", p.nodeID, err) - } - } - n.log.Verbo("The new staking set is:\n%s", n.config.Validators) - } else { - if n.versionCompatibility.WontMask(peerVersion) != nil { - n.maskedValidators.Add(p.nodeID) - } else { - n.maskedValidators.Remove(p.nodeID) - } +func (n *network) NodeUptime() (UptimeResult, bool) { + primaryValidators, ok := n.config.Validators.GetValidators(constants.PrimaryNetworkID) + if !ok { + return UptimeResult{}, false } - // TODO also delete an entry from this map when they leave the validator set - delete(n.latestPeerIP, p.nodeID) - - ip := p.getIP() - n.log.Debug("connected to %s at %s", p.nodeID, ip) - - if !ip.IsZero() { - str := ip.String() - - delete(n.disconnectedIPs, str) - delete(n.retryDelay, str) - n.connectedIPs[str] = struct{}{} + myStake, isValidator := primaryValidators.GetWeight(n.config.MyNodeID) + if !isValidator { + return UptimeResult{}, false } - n.router.Connected(p.nodeID, peerVersion) - n.metrics.connected.Inc() -} - -// should only be called after the peer is marked as connected. -// Assumes [n.stateLock] is not held. -func (n *network) disconnected(p *peer) { - p.net.stateLock.Lock() - defer p.net.stateLock.Unlock() - - ip := p.getIP() - n.log.Debug("disconnected from %s at %s", p.nodeID, ip) - - n.peers.remove(p) - n.metrics.numPeers.Set(float64(n.peers.size())) - - p.releaseAllAliases() + var ( + totalWeight = float64(primaryValidators.Weight()) + totalWeightedPercent = 100 * float64(myStake) + rewardingStake = float64(myStake) + ) - if !ip.IsZero() { - str := ip.String() + n.peersLock.RLock() + defer n.peersLock.RUnlock() - delete(n.disconnectedIPs, str) - delete(n.connectedIPs, str) + for i := 0; i < n.connectedPeers.Len(); i++ { + peer, _ := n.connectedPeers.GetByIndex(i) - if n.config.Validators.Contains(constants.PrimaryNetworkID, p.nodeID) { - n.track(ip, p.nodeID) + nodeID := peer.ID() + weight, ok := primaryValidators.GetWeight(nodeID) + if !ok { + // this is not a validator skip it. + continue } - } - - // Only send Disconnected to router if Connected was sent - if p.finishedHandshake.GetValue() { - n.router.Disconnected(p.nodeID) - } - n.metrics.disconnected.Inc() -} - -// Safe copy the peers dressed as a peerElement -// Assumes [n.stateLock] is not held. -func (n *network) getPeerElements(nodeIDs ids.ShortSet) []*peerElement { - n.stateLock.RLock() - defer n.stateLock.RUnlock() - if n.closed.GetValue() { - return nil - } + observedUptime := peer.ObservedUptime() + percent := float64(observedUptime) + weightFloat := float64(weight) + totalWeightedPercent += percent * weightFloat - peerElements := make([]*peerElement, nodeIDs.Len()) - i := 0 - for nodeID := range nodeIDs { - nodeID := nodeID // Prevent overwrite in next loop iteration - peer, _ := n.peers.getByID(nodeID) // note: peer may be nil - peerElements[i] = &peerElement{ - peer: peer, - id: nodeID, + // if this peer thinks we're above requirement add the weight + if percent/100 >= n.config.UptimeRequirement { + rewardingStake += weightFloat } - i++ - } - return peerElements -} - -// Safe copy the peers -// Assumes [n.stateLock] is not held. -func (n *network) getPeers(nodeIDs ids.ShortSet, subnetID ids.ID, validatorOnly bool) []*peer { - n.stateLock.RLock() - defer n.stateLock.RUnlock() - - if n.closed.GetValue() { - return nil } - var ( - peers = make([]*peer, nodeIDs.Len()) - index int - ) - for nodeID := range nodeIDs { - peer, ok := n.peers.getByID(nodeID) - if ok && - peer.finishedHandshake.GetValue() && - (subnetID == constants.PrimaryNetworkID || peer.trackedSubnets.Contains(subnetID)) && - (!validatorOnly || n.config.Validators.Contains(subnetID, nodeID)) { - peers[index] = peer - } - index++ - } - return peers + return UptimeResult{ + WeightedAveragePercentage: gomath.Abs(totalWeightedPercent / totalWeight), + RewardingStakePercentage: gomath.Abs(100 * rewardingStake / totalWeight), + }, true } -// HealthCheck returns information about several network layer health checks. -// 1) Information about health check results -// 2) An error if the health check reports unhealthy -// Assumes [n.stateLock] is not held -func (n *network) HealthCheck() (interface{}, error) { - // Get some data with the state lock held - connectedTo := 0 - n.stateLock.RLock() - for _, peer := range n.peers.peersList { - if peer != nil && peer.finishedHandshake.GetValue() { - connectedTo++ - } - } - sendFailRate := n.sendFailRateCalculator.Read() - n.stateLock.RUnlock() - - // Make sure we're connected to at least the minimum number of peers - isConnected := connectedTo >= int(n.config.HealthConfig.MinConnectedPeers) - healthy := isConnected - details := map[string]interface{}{ - "connectedPeers": connectedTo, - } - - // Make sure we've received an incoming message within the threshold - now := n.clock.Time() - - lastMsgReceivedAt := time.Unix(atomic.LoadInt64(&n.lastMsgReceivedTime), 0) - timeSinceLastMsgReceived := now.Sub(lastMsgReceivedAt) - isMsgRcvd := timeSinceLastMsgReceived <= n.config.HealthConfig.MaxTimeSinceMsgReceived - healthy = healthy && isMsgRcvd - details["timeSinceLastMsgReceived"] = timeSinceLastMsgReceived.String() - n.metrics.timeSinceLastMsgReceived.Set(float64(timeSinceLastMsgReceived)) +func (n *network) runTimers() { + gossipPeerlists := time.NewTicker(n.config.PeerListGossipFreq) + updateUptimes := time.NewTicker(n.config.UptimeMetricFreq) + defer func() { + gossipPeerlists.Stop() + updateUptimes.Stop() + }() - // Make sure we've sent an outgoing message within the threshold - lastMsgSentAt := time.Unix(atomic.LoadInt64(&n.lastMsgSentTime), 0) - timeSinceLastMsgSent := now.Sub(lastMsgSentAt) - isMsgSent := timeSinceLastMsgSent <= n.config.HealthConfig.MaxTimeSinceMsgSent - healthy = healthy && isMsgSent - details["timeSinceLastMsgSent"] = timeSinceLastMsgSent.String() - n.metrics.timeSinceLastMsgSent.Set(float64(timeSinceLastMsgSent)) + for { + select { + case <-n.onClose: + return + case <-gossipPeerlists.C: + validatorIPs := n.sampleValidatorIPs() + if len(validatorIPs) == 0 { + n.peerConfig.Log.Debug("skipping validator IP gossiping as no IPs are connected") + continue + } - // Make sure the message send failed rate isn't too high - isMsgFailRate := sendFailRate <= n.config.HealthConfig.MaxSendFailRate - healthy = healthy && isMsgFailRate - details["sendFailRate"] = sendFailRate - n.metrics.sendFailRate.Set(sendFailRate) + msg, err := n.peerConfig.MessageCreator.PeerList(validatorIPs, false) + if err != nil { + n.peerConfig.Log.Error( + "failed to gossip %d ips: %s", + len(validatorIPs), + err, + ) + continue + } - // Network layer is unhealthy - if !healthy { - var errorReasons []string - if !isConnected { - errorReasons = append(errorReasons, fmt.Sprintf("not connected to a minimum of %d peer(s) only %d", n.config.HealthConfig.MinConnectedPeers, connectedTo)) - } - if !isMsgRcvd { - errorReasons = append(errorReasons, fmt.Sprintf("no messages from network received in %s > %s", timeSinceLastMsgReceived, n.config.HealthConfig.MaxTimeSinceMsgReceived)) - } - if !isMsgSent { - errorReasons = append(errorReasons, fmt.Sprintf("no messages from network sent in %s > %s", timeSinceLastMsgSent, n.config.HealthConfig.MaxTimeSinceMsgSent)) - } - if !isMsgFailRate { - errorReasons = append(errorReasons, fmt.Sprintf("messages failure send rate %g > %g", sendFailRate, n.config.HealthConfig.MaxSendFailRate)) - } + n.Gossip( + msg, + constants.PrimaryNetworkID, + false, + int(n.config.PeerListValidatorGossipSize), + int(n.config.PeerListNonValidatorGossipSize), + ) - return details, fmt.Errorf("network layer is unhealthy reason: %s", strings.Join(errorReasons, ", ")) - } - return details, nil -} + case <-updateUptimes.C: -// assume [n.stateLock] is held. Returns the timestamp and signature that should -// be sent in a Version message. We only update these values when our IP has -// changed. -func (n *network) getVersion(ip utils.IPDesc) (uint64, []byte, error) { - n.timeForIPLock.Lock() - defer n.timeForIPLock.Unlock() - - if !ip.Equal(n.lastVersionIP) { - newTimestamp := n.clock.Unix() - msgHash := ipAndTimeHash(ip, newTimestamp) - sig, err := n.config.TLSKey.Sign(cryptorand.Reader, msgHash, crypto.SHA256) - if err != nil { - return 0, nil, err + result, _ := n.NodeUptime() + n.metrics.nodeUptimeWeightedAverage.Set(result.WeightedAveragePercentage) + n.metrics.nodeUptimeRewardingStake.Set(result.RewardingStakePercentage) } - - n.lastVersionIP = ip - n.lastVersionTimestamp = newTimestamp - n.lastVersionSignature = sig } - - return n.lastVersionTimestamp, n.lastVersionSignature, nil } diff --git a/network/network_test.go b/network/network_test.go index 08f1d767b773..c03c806e91b0 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -4,13 +4,7 @@ package network import ( - "context" "crypto" - "crypto/tls" - "crypto/x509" - "errors" - "math" - "net" "sync" "testing" "time" @@ -27,3185 +21,266 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/snow/uptime" "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/staking" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/version" ) -const ( - defaultAliasTimeout = 2 * time.Second - defaultGossipPeerListFreq = time.Minute - defaultPeerListSize = 50 - defaultGossipPeerListTo = 100 - defaultGossipAcceptedFrontierSize = 35 - defaultAppGossipNonValidatorSize = 2 - defaultAppGossipValidatorSize = 4 -) - var ( - errClosed = errors.New("closed") - errRefused = errors.New("connection refused") - - testSubnetID = ids.GenerateTestID() - defaulAppVersion = version.NewDefaultApplication("app", 0, 1, 0) - defaultVersionManager = version.NewCompatibility( - defaulAppVersion, - defaulAppVersion, - time.Now(), - defaulAppVersion, - defaulAppVersion, - time.Now(), - defaulAppVersion, - ) -) - -type testListener struct { - addr net.Addr - inbound chan net.Conn - once sync.Once - closed chan struct{} -} - -func getDefaultManager() validators.Manager { - defaultValidators := validators.NewManager() - err := defaultValidators.Set(constants.PrimaryNetworkID, validators.NewSet()) - if err != nil { - panic(err) - } - return defaultValidators -} - -func (l *testListener) Accept() (net.Conn, error) { - select { - case c := <-l.inbound: - return c, nil - case <-l.closed: - return nil, errClosed - } -} - -func (l *testListener) Close() error { - l.once.Do(func() { close(l.closed) }) - return nil -} - -func (l *testListener) Addr() net.Addr { return l.addr } - -type testDialer struct { - addr net.Addr - - outbounds map[string]*testListener - outboundsLock sync.Mutex - - // clients tracks opened client connections - // by IP - clients map[string]*testConn - - // closer is invoked when testDialer is closed - closer func(net.Addr, net.Addr) -} - -func (d *testDialer) Dial(ctx context.Context, ip utils.IPDesc) (net.Conn, error) { - d.outboundsLock.Lock() - defer d.outboundsLock.Unlock() - - outbound, ok := d.outbounds[ip.String()] - if !ok { - return nil, errRefused - } - server := &testConn{ - pendingReads: make(chan []byte, 1<<10), - pendingWrites: make(chan []byte, 1<<10), - closed: make(chan struct{}), - local: outbound.addr, - remote: d.addr, - closer: d.closer, - } - client := &testConn{ - pendingReads: server.pendingWrites, - pendingWrites: server.pendingReads, - closed: make(chan struct{}), - local: d.addr, - remote: outbound.addr, - closer: d.closer, - } - - select { - case outbound.inbound <- server: - if d.clients == nil { - d.clients = make(map[string]*testConn) - } - d.clients[ip.String()] = client - return client, nil - default: - return nil, errRefused - } -} - -func (d *testDialer) Update(ip utils.DynamicIPDesc, listener *testListener) { - d.outboundsLock.Lock() - defer d.outboundsLock.Unlock() - - d.outbounds[ip.String()] = listener -} - -type testConn struct { - partialRead []byte - pendingReads chan []byte - pendingWrites chan []byte - closed chan struct{} - once sync.Once - - // closer is invoked when testConn is closed - closer func(net.Addr, net.Addr) - - local, remote net.Addr -} - -func (c *testConn) Read(b []byte) (int, error) { - for len(c.partialRead) == 0 { - select { - case read, ok := <-c.pendingReads: - if !ok { - return 0, errClosed - } - c.partialRead = read - case <-c.closed: - return 0, errClosed - } - } - - copy(b, c.partialRead) - if length := len(c.partialRead); len(b) > length { - c.partialRead = nil - return length, nil - } - c.partialRead = c.partialRead[len(b):] - return len(b), nil -} - -func (c *testConn) Write(b []byte) (int, error) { - newB := make([]byte, len(b)) - copy(newB, b) - - select { - case c.pendingWrites <- newB: - case <-c.closed: - return 0, errClosed - } - - return len(b), nil -} - -func (c *testConn) Close() error { - c.once.Do(func() { - close(c.closed) - - if c.closer != nil { - c.closer(c.local, c.remote) - } - }) - return nil -} - -func (c *testConn) LocalAddr() net.Addr { return c.local } -func (c *testConn) RemoteAddr() net.Addr { return c.remote } -func (c *testConn) SetDeadline(time.Time) error { return nil } -func (c *testConn) SetReadDeadline(time.Time) error { return nil } -func (c *testConn) SetWriteDeadline(time.Time) error { return nil } - -type testHandler struct { - router.Router - ConnectedF func(nodeID ids.ShortID, nodeVersion version.Application) - DisconnectedF func(nodeID ids.ShortID) - PutF func( - validatorID ids.ShortID, - chainID ids.ID, - requestID uint32, - containerID ids.ID, - container []byte, - onFinishedHandling func(), - ) - AppGossipF func( - nodeID ids.ShortID, - chainID ids.ID, - appGossipBytes []byte, - onFinishedHandling func(), - ) -} - -func (h *testHandler) Connected(id ids.ShortID, nodeVersion version.Application) { - if h.ConnectedF != nil { - h.ConnectedF(id, nodeVersion) - } -} - -func (h *testHandler) Disconnected(id ids.ShortID) { - if h.DisconnectedF != nil { - h.DisconnectedF(id) - } -} - -func (h *testHandler) HandleInbound(msg message.InboundMessage) { - switch msg.Op() { - case message.Put: - chainID, _ := ids.ToID(msg.Get(message.ChainID).([]byte)) - requestID, _ := msg.Get(message.RequestID).(uint32) - containerID, _ := ids.ToID(msg.Get(message.ContainerID).([]byte)) - container, _ := msg.Get(message.ContainerBytes).([]byte) - - if h.PutF != nil { - h.PutF( - msg.NodeID(), - chainID, - requestID, - containerID, - container, - msg.OnFinishedHandling, - ) - } - case message.AppGossip: - chainID, _ := ids.ToID(msg.Get(message.ChainID).([]byte)) - appBytes := msg.Get(message.AppBytes).([]byte) - - if h.AppGossipF != nil { - h.AppGossipF(msg.NodeID(), chainID, appBytes, msg.OnFinishedHandling) - } - default: - return - } -} - -// testUpgrader is used to provide a custom -// id for a given IP -type testUpgrader struct { - // ids is a mapping of IP addresses - // to id - ids map[string]ids.ShortID - certs map[string]*x509.Certificate - idsLock sync.Mutex -} - -func (u *testUpgrader) Upgrade(conn net.Conn) (ids.ShortID, net.Conn, *x509.Certificate, error) { - u.idsLock.Lock() - defer u.idsLock.Unlock() - addr := conn.RemoteAddr() - str := addr.String() - return u.ids[str], conn, u.certs[str], nil -} + defaultHealthConfig = HealthConfig{ + MinConnectedPeers: 1, + MaxTimeSinceMsgReceived: time.Minute, + MaxTimeSinceMsgSent: time.Minute, + MaxPortionSendQueueBytesFull: .9, + MaxSendFailRate: .1, + SendFailRateHalflife: time.Second, + } + defaultPeerListGossipConfig = PeerListGossipConfig{ + PeerListNumValidatorIPs: 100, + PeerListValidatorGossipSize: 100, + PeerListNonValidatorGossipSize: 100, + PeerListGossipFreq: time.Second, + } + defaultTimeoutConfig = TimeoutConfig{ + PingPongTimeout: 30 * time.Second, + ReadHandshakeTimeout: 15 * time.Second, + } + defaultDelayConfig = DelayConfig{ + MaxReconnectDelay: time.Hour, + InitialReconnectDelay: time.Second, + } + defaultThrottlerConfig = ThrottlerConfig{ + InboundConnUpgradeThrottlerConfig: throttling.InboundConnUpgradeThrottlerConfig{ + UpgradeCooldown: time.Second, + MaxRecentConnsUpgraded: 100, + }, + InboundMsgThrottlerConfig: throttling.InboundMsgThrottlerConfig{ + MsgByteThrottlerConfig: throttling.MsgByteThrottlerConfig{ + VdrAllocSize: 1 * units.GiB, + AtLargeAllocSize: 1 * units.GiB, + NodeMaxAtLargeBytes: constants.DefaultMaxMessageSize, + }, + BandwidthThrottlerConfig: throttling.BandwidthThrottlerConfig{ + RefillRate: units.MiB, + MaxBurstSize: constants.DefaultMaxMessageSize, + }, + MaxProcessingMsgsPerNode: 100, + }, + OutboundMsgThrottlerConfig: throttling.MsgByteThrottlerConfig{ + VdrAllocSize: 1 * units.GiB, + AtLargeAllocSize: 1 * units.GiB, + NodeMaxAtLargeBytes: constants.DefaultMaxMessageSize, + }, + MaxInboundConnsPerSec: 100, + } + defaultDialerConfig = dialer.Config{ + ThrottleRps: 100, + ConnectionTimeout: time.Second, + } + defaultConfig = Config{ + HealthConfig: defaultHealthConfig, + PeerListGossipConfig: defaultPeerListGossipConfig, + TimeoutConfig: defaultTimeoutConfig, + DelayConfig: defaultDelayConfig, + ThrottlerConfig: defaultThrottlerConfig, + + DialerConfig: defaultDialerConfig, + + Namespace: "", + NetworkID: 49463, + MaxClockDifference: time.Minute, + PingFrequency: constants.DefaultPingFrequency, + AllowPrivateIPs: true, -func (u *testUpgrader) Update(ip utils.DynamicIPDesc, id ids.ShortID) { - u.idsLock.Lock() - defer u.idsLock.Unlock() + CompressionEnabled: true, - u.ids[ip.String()] = id -} + UptimeCalculator: uptime.NewManager(uptime.NewTestState()), + UptimeMetricFreq: 30 * time.Second, + UptimeRequirement: .8, -var ( - certLock sync.Mutex - cert0 *tls.Certificate - tlsConfig0 *tls.Config - cert1 *tls.Certificate - tlsConfig1 *tls.Config - cert2 *tls.Certificate - tlsConfig2 *tls.Config -) + RequireValidatorToConnect: false, -func initCerts(t *testing.T) { - certLock.Lock() - defer certLock.Unlock() - if cert0 != nil { - return + MaximumInboundMessageTimeout: 30 * time.Second, } - var err error - cert0, err = staking.NewTLSCert() - assert.NoError(t, err) - tlsConfig0 = TLSConfig(*cert0) - cert1, err = staking.NewTLSCert() - assert.NoError(t, err) - tlsConfig1 = TLSConfig(*cert1) - cert2, err = staking.NewTLSCert() - assert.NoError(t, err) - tlsConfig2 = TLSConfig(*cert2) -} - -var ( - defaultInboundMsgThrottler = throttling.NewNoInboundThrottler() - defaultOutboundMsgThrottler = throttling.NewNoOutboundThrottler() ) -func TestNewDefaultNetwork(t *testing.T) { - initCerts(t) - - ip := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id := ids.ShortID(hashing.ComputeHash160Array([]byte(ip.IP().String()))) - - listener := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - - vdrs := getDefaultManager() - beacons := validators.NewSet() - metrics := prometheus.NewRegistry() - msgCreator, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler := &testHandler{} - net, err := newDefaultNetwork( - id, - ip, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener, - metrics, - msgCreator, - handler, - ) - assert.NoError(t, err) - assert.NotNil(t, net) - - go func() { - err := net.Close() - assert.NoError(t, err) - }() - - err = net.Dispatch() - assert.Error(t, err) -} - -func TestEstablishConnection(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - +func newTestNetwork(t *testing.T, count int) (*testDialer, []*testListener, []ids.ShortID, []*Config) { var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup + dialer = newTestDialer() + listeners = make([]*testListener, count) + nodeIDs = make([]ids.ShortID, count) + configs = make([]*Config, count) ) - wg0.Add(1) - wg1.Add(1) + for i := 0; i < count; i++ { + ip, listener := dialer.NewListener() + nodeID, tlsCert, tlsConfig := getTLS(t, i) - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id0 { - wg0.Done() - } - }, - } + config := defaultConfig + config.TLSConfig = tlsConfig + config.MyNodeID = nodeID + config.MyIP = ip + config.TLSKey = tlsCert.PrivateKey.(crypto.Signer) - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id1 { - wg1.Done() - } - }, + listeners[i] = listener + nodeIDs[i] = nodeID + configs[i] = &config } + return dialer, listeners, nodeIDs, configs +} - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, +func newMessageCreator(t *testing.T) message.Creator { + t.Helper() + mc, err := message.NewCreator( + prometheus.NewRegistry(), + true, + "", + 10*time.Second, ) assert.NoError(t, err) - assert.NotNil(t, net1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - net0.Track(ip1.IP(), id1) - - wg0.Wait() - wg1.Wait() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) + return mc } -func TestDoubleTrack(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } +func newFullyConnectedTestNetwork(t *testing.T, handlers []router.InboundHandler) ([]ids.ShortID, []Network, *sync.WaitGroup) { + assert := assert.New(t) - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 + dialer, listeners, nodeIDs, configs := newTestNetwork(t, len(handlers)) - vdrs := getDefaultManager() beacons := validators.NewSet() + err := beacons.AddWeight(nodeIDs[0], 1) + assert.NoError(err) - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - ) - wg0.Add(1) - wg1.Add(1) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id0 { - wg0.Done() - } - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id1 { - wg1.Done() - } - }, - } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net0.Track(ip1.IP(), id1) - net0.Track(ip1.IP(), id1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - wg0.Wait() - wg1.Wait() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) -} - -func TestDoubleClose(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), + vdrs := validators.NewManager() + for _, nodeID := range nodeIDs { + err := vdrs.AddWeight(constants.PrimaryNetworkID, nodeID, 1) + assert.NoError(err) } - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - - vdrs := getDefaultManager() - beacons := validators.NewSet() + msgCreator := newMessageCreator(t) var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - ) - wg0.Add(1) - wg1.Add(1) + networks = make([]Network, len(configs)) + + globalLock sync.Mutex + numConnected int + allConnected bool + onAllConnected = make(chan struct{}) + ) + for i, config := range configs { + config := config + + config.Beacons = beacons + config.Validators = vdrs + + var connected ids.ShortSet + net, err := NewNetwork( + config, + msgCreator, + prometheus.NewRegistry(), + logging.NoLog{}, + listeners[i], + dialer, + &testHandler{ + InboundHandler: handlers[i], + ConnectedF: func(nodeID ids.ShortID, _ version.Application) { + t.Logf("%s connected to %s", config.MyNodeID, nodeID) + + globalLock.Lock() + defer globalLock.Unlock() + + assert.False(connected.Contains(nodeID)) + connected.Add(nodeID) + numConnected++ + + if !allConnected && numConnected == len(nodeIDs)*(len(nodeIDs)-1) { + allConnected = true + close(onAllConnected) + } + }, + DisconnectedF: func(nodeID ids.ShortID) { + t.Logf("%s disconnected from %s", config.MyNodeID, nodeID) + + globalLock.Lock() + defer globalLock.Unlock() + + assert.True(connected.Contains(nodeID)) + connected.Remove(nodeID) + numConnected-- + }, + }, + benchlist.NewManager(&benchlist.Config{}), + ) + assert.NoError(err) + networks[i] = net + } + + wg := sync.WaitGroup{} + wg.Add(len(networks)) + for i, net := range networks { + if i != 0 { + config := configs[0] + net.ManuallyTrack(config.MyNodeID, config.MyIP.IP()) + } - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id0 { - wg0.Done() - } - }, - } + go func(net Network) { + defer wg.Done() - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id1 { - wg1.Done() - } - }, + err := net.Dispatch() + assert.NoError(err) + }(net) } - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net0.Track(ip1.IP(), id1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - wg0.Wait() - wg1.Wait() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) -} - -func TestTrackConnected(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), + if len(networks) > 1 { + <-onAllConnected } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - ) - wg0.Add(1) - wg1.Add(1) + return nodeIDs, networks, &wg +} - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id0 { - wg0.Done() - } - }, - } +func TestNewNetwork(t *testing.T) { + _, networks, wg := newFullyConnectedTestNetwork(t, []router.InboundHandler{nil, nil, nil}) - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id1 { - wg1.Done() - } - }, + for _, net := range networks { + net.StartClose() } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net0.Track(ip1.IP(), id1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - wg0.Wait() - wg1.Wait() - - net0.Track(ip1.IP(), id1) - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) + wg.Wait() } -func TestTrackConnectedRace(t *testing.T) { - initCerts(t) +func TestSend(t *testing.T) { + assert := assert.New(t) - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, + received := make(chan message.InboundMessage) + nodeIDs, networks, wg := newFullyConnectedTestNetwork( + t, + []router.InboundHandler{ + router.InboundHandlerFunc(func(message.InboundMessage) { + t.Fatal("unexpected message received") + }), + router.InboundHandlerFunc(func(msg message.InboundMessage) { + received <- msg + }), + router.InboundHandlerFunc(func(message.InboundMessage) { + t.Fatal("unexpected message received") + }), + }, ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } + net0 := networks[0] - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 + mc := newMessageCreator(t) + outboundGetMsg, err := mc.Get(ids.Empty, 1, time.Second, ids.Empty) + assert.NoError(err) - vdrs := getDefaultManager() - beacons := validators.NewSet() - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) + toSend := ids.ShortSet{} + toSend.Add(nodeIDs[1]) + sentTo := net0.Send(outboundGetMsg, toSend, constants.PrimaryNetworkID, false) + assert.EqualValues(toSend, sentTo) - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) + inboundGetMsg := <-received + assert.Equal(message.Get, inboundGetMsg.Op()) - handler := &testHandler{} - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net0.Track(ip1.IP(), id1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) -} - -func assertEqualPeers(t *testing.T, expected map[string]ids.ShortID, actual []PeerInfo) { - assert.Len(t, actual, len(expected)) - for _, p := range actual { - match, ok := expected[p.IP] - assert.True(t, ok, "peer with IP %s missing", p.IP) - assert.Equal(t, match.PrefixedString(constants.NodeIDPrefix), p.ID) - } -} - -func TestPeerAliasesTicker(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := certToID(cert0.Leaf) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := certToID(cert1.Leaf) - ip2 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 2, - ) - id2 := certToID(cert2.Leaf) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - listener2 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller2 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - listener3 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller3 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller0.outbounds[ip2.IP().String()] = listener2 - caller1.outbounds[ip0.IP().String()] = listener0 - caller2.outbounds[ip0.IP().String()] = listener0 - caller3.outbounds[ip0.IP().String()] = listener0 - - upgrader := &testUpgrader{ - ids: map[string]ids.ShortID{ - ip0.IP().String(): id0, - ip1.IP().String(): id1, - ip2.IP().String(): id1, - }, - certs: map[string]*x509.Certificate{ - ip0.IP().String(): cert0.Leaf, - ip1.IP().String(): cert1.Leaf, - ip2.IP().String(): cert2.Leaf, - }, - } - - vdrs := getDefaultManager() - beacons := validators.NewSet() - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - wg1Done bool - wg2 sync.WaitGroup - - cleanup bool - ) - wg0.Add(2) - wg1.Add(1) - wg2.Add(2) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id1 { - wg0.Done() - return - } - if id == id2 { - wg2.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 0 unauthorized connection", id.String()) - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id0 { - wg0.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 1 unauthorized connection", id.String()) - }, - } - - metrics2 := prometheus.NewRegistry() - msgCreator2, err := message.NewCreator(metrics2, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler2 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if cleanup { - return - } - - assert.Fail(t, "handler 2 unauthorized connection", id.String()) - }, - } - - metrics3 := prometheus.NewRegistry() - msgCreator3, err := message.NewCreator(metrics3, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler3 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id0 { - wg2.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 3 unauthorized connection", id.String()) - }, - } - - caller0.closer = func(local net.Addr, remote net.Addr) { - if remote.String() == ip2.String() && !wg1Done { - wg1.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "caller 0 unauthorized close", local.String(), remote.String()) - } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - netw0 := net0.(*network) - - netw0.serverUpgrader = upgrader - netw0.clientUpgrader = upgrader - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - netw1 := net1.(*network) - - netw1.serverUpgrader = upgrader - netw1.clientUpgrader = upgrader - - net2, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig2, - listener2, - caller2, - metrics2, - msgCreator2, - handler2, - ) - assert.NoError(t, err) - assert.NotNil(t, net2) - - netw2 := net2.(*network) - - netw2.serverUpgrader = upgrader - netw2.clientUpgrader = upgrader - - net3, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig2, - listener3, - caller3, - metrics3, - msgCreator3, - handler3, - ) - assert.NoError(t, err) - assert.NotNil(t, net3) - - netw3 := net3.(*network) - - netw3.serverUpgrader = upgrader - netw3.clientUpgrader = upgrader - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net2.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net3.Dispatch() - assert.Error(t, err) - }() - - // Connect to peer with id1 - net0.Track(ip1.IP(), id1) - - // Confirm peers correct - wg0.Wait() - assertEqualPeers(t, map[string]ids.ShortID{ - ip1.String(): id1, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assert.Len(t, net3.Peers(nil), 0) - - // Attempt to connect to ip2 (same id as ip1) - net0.Track(ip2.IP(), id2) - - // Confirm that ip2 was not added to net0 peers - wg1.Wait() - wg1Done = true - assertEqualPeers(t, map[string]ids.ShortID{ - ip1.String(): id1, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assert.Len(t, net3.Peers(nil), 0) - - // Subsequent track call returns immediately with no connection attempts - // (would cause fatal error from unauthorized connection if allowed) - net0.Track(ip2.IP(), id2) - - // Wait for aliases to be removed by peer - time.Sleep(3 * time.Second) - - // Track ip2 on net3 - upgrader.Update(ip2, id2) - caller0.Update(ip2, listener3) - net0.Track(ip2.IP(), id2) - - // Confirm that id2 was added as peer - wg2.Wait() - assertEqualPeers(t, map[string]ids.ShortID{ - ip1.String(): id1, - ip2.String(): id2, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net3.Peers(nil)) - - // Cleanup - cleanup = true - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net2.Close() - assert.NoError(t, err) - - err = net3.Close() - assert.NoError(t, err) -} - -func TestPeerAliasesDisconnect(t *testing.T) { - initCerts(t) - - vdrs := getDefaultManager() - beacons := validators.NewSet() - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - ip2 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 2, - ) - id2 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip2.IP().String()))) - - err := vdrs.AddWeight(constants.PrimaryNetworkID, id0, 1) - if err != nil { - t.Fatal(err) - } - - err = vdrs.AddWeight(constants.PrimaryNetworkID, id1, 1) - if err != nil { - t.Fatal(err) - } - - err = vdrs.AddWeight(constants.PrimaryNetworkID, id2, 1) - if err != nil { - t.Fatal(err) - } - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - listener2 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller2 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - listener3 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller3 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller0.outbounds[ip2.IP().String()] = listener2 - caller1.outbounds[ip0.IP().String()] = listener0 - caller2.outbounds[ip0.IP().String()] = listener0 - caller3.outbounds[ip0.IP().String()] = listener0 - - upgrader := &testUpgrader{ - ids: map[string]ids.ShortID{ - ip0.IP().String(): id0, - ip1.IP().String(): id1, - ip2.IP().String(): id1, - }, - certs: map[string]*x509.Certificate{ - ip0.IP().String(): cert0.Leaf, - ip1.IP().String(): cert1.Leaf, - ip2.IP().String(): cert2.Leaf, - }, - } - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - wg1Done bool - wg2 sync.WaitGroup - wg2Done bool - wg3 sync.WaitGroup - - cleanup bool - ) - wg0.Add(2) - wg1.Add(1) - wg2.Add(3) - wg3.Add(2) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id1 { - wg0.Done() - return - } - if id == id2 { - wg3.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 0 unauthorized connection", id.String()) - }, - DisconnectedF: func(id ids.ShortID) { - if id == id1 { - wg2.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 0 unauthorized disconnect", id.String()) - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id0 { - wg0.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 1 unauthorized connection", id.String()) - }, - } - - metrics2 := prometheus.NewRegistry() - msgCreator2, err := message.NewCreator(metrics2, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler2 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if cleanup { - return - } - - assert.Fail(t, "handler 2 unauthorized connection", id.String()) - }, - } - - metrics3 := prometheus.NewRegistry() - msgCreator3, err := message.NewCreator(metrics3, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler3 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id == id0 { - wg3.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "handler 3 unauthorized connection", id.String()) - }, - } - - caller0.closer = func(local net.Addr, remote net.Addr) { - if remote.String() == ip2.String() && !wg1Done { - wg1.Done() - return - } - if remote.String() == ip1.String() && !wg2Done { - wg2.Done() - return - } - if local.String() == ip1.String() && !wg2Done { - wg2.Done() - return - } - if cleanup { - return - } - - assert.Fail(t, "caller 0 unauthorized close", local.String(), remote.String()) - } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - netw0 := net0.(*network) - netw0.serverUpgrader = upgrader - netw0.clientUpgrader = upgrader - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - netw1 := net1.(*network) - netw1.serverUpgrader = upgrader - netw1.clientUpgrader = upgrader - - net2, err := newTestNetwork( - id1, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig2, - listener2, - caller2, - metrics2, - msgCreator2, - handler2, - ) - assert.NoError(t, err) - assert.NotNil(t, net2) - - netw2 := net2.(*network) - netw2.serverUpgrader = upgrader - netw2.clientUpgrader = upgrader - - net3, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig2, - listener3, - caller3, - metrics3, - msgCreator3, - handler3, - ) - assert.NoError(t, err) - assert.NotNil(t, net3) - - netw3 := net3.(*network) - netw3.serverUpgrader = upgrader - netw3.clientUpgrader = upgrader - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net2.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net3.Dispatch() - assert.Error(t, err) - }() - - // Connect to peer with id1 - net0.Track(ip1.IP(), id1) - - // Confirm peers correct - wg0.Wait() - assertEqualPeers(t, map[string]ids.ShortID{ - ip1.String(): id1, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assert.Len(t, net3.Peers(nil), 0) - - // Attempt to connect to ip2 (same id as ip1) - net0.Track(ip2.IP(), id2) - - // Confirm that ip2 was not added to net0 peers - wg1.Wait() - wg1Done = true - assertEqualPeers(t, map[string]ids.ShortID{ - ip1.String(): id1, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assert.Len(t, net3.Peers(nil), 0) - - // Disconnect original peer - _ = caller0.clients[ip1.String()].Close() - - // Track ip2 on net3 - wg2.Wait() - wg2Done = true - assertEqualPeers(t, map[string]ids.ShortID{}, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assert.Len(t, net3.Peers(nil), 0) - upgrader.Update(ip2, id2) - caller0.Update(ip2, listener3) - net0.Track(ip2.IP(), id2) - - // Confirm that id2 was added as peer - wg3.Wait() - assertEqualPeers(t, map[string]ids.ShortID{ - ip2.String(): id2, - }, net0.Peers(nil)) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net1.Peers(nil)) - assert.Len(t, net2.Peers(nil), 0) - assertEqualPeers(t, map[string]ids.ShortID{ - ip0.String(): id0, - }, net3.Peers(nil)) - - // Cleanup - cleanup = true - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net2.Close() - assert.NoError(t, err) - - err = net3.Close() - assert.NoError(t, err) -} - -func TestPeerSignature(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - ip2 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 2, - ) - - id0 := certToID(cert0.Leaf) - id1 := certToID(cert1.Leaf) - id2 := certToID(cert2.Leaf) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - listener2 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller2 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - caller0.outbounds[ip2.IP().String()] = listener2 - caller1.outbounds[ip2.IP().String()] = listener2 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - // id2 is a validator - err := vdrs.AddWeight(constants.PrimaryNetworkID, id2, math.MaxUint64) - assert.NoError(t, err) - - allPeers := ids.ShortSet{} - allPeers.Add(id0, id1, id2) - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - wg2 sync.WaitGroup - ) - wg0.Add(2) - wg1.Add(2) - wg2.Add(2) - - handledLock := sync.RWMutex{} - handled := make(map[string]struct{}) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id0 { - handledLock.Lock() - handled[id0.String()+":"+id.String()] = struct{}{} - handledLock.Unlock() - wg0.Done() - } - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id1 { - handledLock.Lock() - handled[id1.String()+":"+id.String()] = struct{}{} - handledLock.Unlock() - wg1.Done() - } - }, - } - - metrics2 := prometheus.NewRegistry() - msgCreator2, err := message.NewCreator(metrics2, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler2 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - if id != id2 { - handledLock.Lock() - handled[id2.String()+":"+id.String()] = struct{}{} - handledLock.Unlock() - wg2.Done() - } - }, - } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net2, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig2, - listener2, - caller2, - metrics2, - msgCreator2, - handler2, - ) - assert.NoError(t, err) - assert.NotNil(t, net2) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net2.Dispatch() - assert.Error(t, err) - }() - - // ip0 -> ip2 and ip0 -> ip1 connect - net0.Track(ip2.IP(), id2) - net0.Track(ip1.IP(), id1) - - // now we force send peer message from ip0 - // this should cause a peer ip1 -> ip2 (ip2 is a validator) - for { - handledLock.RLock() - lenhan := len(handled) - handledLock.RUnlock() - if lenhan == 6 { - break - } - peers := net0.(*network).getPeerElements(allPeers) - for _, p := range peers { - if p.peer == nil { - continue - } - p.peer.sendPeerList() - } - time.Sleep(500 * time.Millisecond) - } - wg0.Wait() - wg1.Wait() - wg2.Wait() - - _, ok := handled[id0.String()+":"+id1.String()] - assert.True(t, ok) - _, ok = handled[id0.String()+":"+id2.String()] - assert.True(t, ok) - _, ok = handled[id1.String()+":"+id0.String()] - assert.True(t, ok) - _, ok = handled[id1.String()+":"+id2.String()] - assert.True(t, ok) - _, ok = handled[id2.String()+":"+id0.String()] - assert.True(t, ok) - _, ok = handled[id2.String()+":"+id1.String()] - assert.True(t, ok) - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net2.Close() - assert.NoError(t, err) -} - -func TestValidatorIPs(t *testing.T) { - netConfig := newDefaultConfig() - dummyNetwork := network{config: &netConfig} - dummyNetwork.config.PeerListSize = 50 - - appVersion := version.NewDefaultApplication("app", 1, 1, 0) - versionManager := version.NewCompatibility( - appVersion, - appVersion, - time.Now(), - appVersion, - appVersion, - time.Now(), - appVersion, - ) - dummyNetwork.versionCompatibility = versionManager - - // SCENARIO: Connected validator peers with right version and cert are picked - // context - clearPeersData(&dummyNetwork) - firstValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 1), - Port: 1, - } - firstValidatorPeer := createPeer(ids.ShortID{0x01}, firstValidatorIPDesc, appVersion) - addPeerToNetwork(&dummyNetwork, firstValidatorPeer, true) - - secondValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 2), - Port: 2, - } - secondValidatorPeer := createPeer(ids.ShortID{0x02}, secondValidatorIPDesc, appVersion) - addPeerToNetwork(&dummyNetwork, secondValidatorPeer, true) - - thirdValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 3), - Port: 3, - } - thirdValidatorPeer := createPeer(ids.ShortID{0x03}, thirdValidatorIPDesc, appVersion) - addPeerToNetwork(&dummyNetwork, thirdValidatorPeer, true) - - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, firstValidatorPeer.nodeID)) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, secondValidatorPeer.nodeID)) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, thirdValidatorPeer.nodeID)) - - // test - validatorIPs, err := dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 3) - assert.True(t, isIPDescIn(firstValidatorPeer.getIP(), validatorIPs)) - assert.True(t, isIPDescIn(secondValidatorPeer.getIP(), validatorIPs)) - assert.True(t, isIPDescIn(thirdValidatorPeer.getIP(), validatorIPs)) - - // SCENARIO: no peers case is handled - // context - clearPeersData(&dummyNetwork) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: validators not connected are not picked - // context - clearPeersData(&dummyNetwork) - disconnectedValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 4), - Port: 4, - } - disconnectedValidatorPeer := createPeer(ids.ShortID{0x01}, disconnectedValidatorIPDesc, appVersion) - disconnectedValidatorPeer.finishedHandshake.SetValue(false) - addPeerToNetwork(&dummyNetwork, disconnectedValidatorPeer, true) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, disconnectedValidatorPeer.nodeID)) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: validators with zeroed IP are not picked - // context - clearPeersData(&dummyNetwork) - zeroIPValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4zero, - Port: 1, - } - zeroValidatorPeer := createPeer(ids.ShortID{0x01}, zeroIPValidatorIPDesc, appVersion) - addPeerToNetwork(&dummyNetwork, zeroValidatorPeer, true) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, zeroValidatorPeer.nodeID)) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: Non-validator peer not selected - // context - clearPeersData(&dummyNetwork) - nonValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 5), - Port: 5, - } - - nonValidatorPeer := createPeer(ids.ShortID{0x04}, nonValidatorIPDesc, appVersion) - addPeerToNetwork(&dummyNetwork, nonValidatorPeer, false) - assert.False(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, nonValidatorPeer.nodeID)) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: validators with wrong version are not picked - // context - clearPeersData(&dummyNetwork) - maskedVersion := version.NewDefaultApplication("app", 0, 1, 0) - - maskedValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 6), - Port: 6, - } - maskedValidatorPeer := createPeer(ids.ShortID{0x01}, maskedValidatorIPDesc, maskedVersion) - addPeerToNetwork(&dummyNetwork, maskedValidatorPeer, true) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, maskedValidatorPeer.nodeID)) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: validators with wrong certificate are not picked - // context - clearPeersData(&dummyNetwork) - wrongCertValidatorIPDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 7), - Port: 7, - } - ipOnCert := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, 8), - Port: 8, - } - wrongCertValidatorPeer := createPeer(ids.ShortID{0x01}, wrongCertValidatorIPDesc, appVersion) - wrongCertValidatorPeer.sigAndTime.SetValue(signedPeerIP{ - ip: ipOnCert, - time: uint64(0), - }) - addPeerToNetwork(&dummyNetwork, wrongCertValidatorPeer, true) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, wrongCertValidatorPeer.nodeID)) - - // test - validatorIPs, err = dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(validatorIPs) == 0) - - // SCENARIO: At most peerListSize validators are picked - // context - clearPeersData(&dummyNetwork) - dummyNetwork.config.PeerListSize = 2 - - validPeerCount := dummyNetwork.config.PeerListSize * 2 - for i := 0; i < int(validPeerCount); i++ { - ipDesc := utils.IPDesc{ - IP: net.IPv4(172, 17, 0, byte(i)), - Port: uint16(i), - } - peer := createPeer(ids.ShortID{byte(i)}, ipDesc, appVersion) - addPeerToNetwork(&dummyNetwork, peer, true) - assert.True(t, dummyNetwork.config.Validators.Contains(constants.PrimaryNetworkID, peer.nodeID)) - } - - // test - IPs, err := dummyNetwork.validatorIPs() - - // checks - assert.NoError(t, err) - assert.True(t, len(IPs) == int(dummyNetwork.config.PeerListSize)) -} - -// Test that a node will not finish the handshake if the peer's version -// is incompatible -func TestDontFinishHandshakeOnIncompatibleVersion(t *testing.T) { - initCerts(t) - - // Node 0 considers node 1 incompatible - net0Version := version.NewDefaultApplication("app", 1, 4, 7) - net0MinCompatibleVersion := version.NewDefaultApplication("app", 1, 4, 5) - // Node 1 considers node 0 compatible - net1Version := version.NewDefaultApplication("app", 1, 4, 4) - net1MinCompatibleVersion := version.NewDefaultApplication("app", 1, 4, 4) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - - id0 := certToID(cert0.Leaf) - id1 := certToID(cert1.Leaf) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - closer: func(net.Addr, net.Addr) { listener0.Close() }, - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - assert.NoError(t, vdrs.AddWeight(constants.PrimaryNetworkID, id1, 1)) - assert.NoError(t, vdrs.AddWeight(constants.PrimaryNetworkID, id0, 1)) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - net0Compatibility := version.NewCompatibility( - net0Version, - net0MinCompatibleVersion, - time.Now(), - net0MinCompatibleVersion, - net0MinCompatibleVersion, - time.Now(), - net0MinCompatibleVersion, - ) - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - net1Compatibility := version.NewCompatibility( - net1Version, - net1MinCompatibleVersion, - time.Now(), - net1MinCompatibleVersion, - net1MinCompatibleVersion, - time.Now(), - net1MinCompatibleVersion, - ) - - net0, err := newTestNetwork( - id0, - ip0, - net0Compatibility, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - &testHandler{}, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - net1Compatibility, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - &testHandler{}, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - // net1 connects to net0 - // they start the handshake and exchange versions - // net1 sees net0 as incompatible and closes the connection - net1.Track(ip0.IP(), id0) - - select { - case <-time.After(5 * time.Second): - t.Error("should have closed immediately because net1 sees net0 as incompatible") - case <-listener0.closed: - } - - // Cleanup - err = net0.Close() - assert.NoError(t, err) - err = net1.Close() - assert.NoError(t, err) -} - -func TestPeerTrackedSubnets(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id0 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip0.IP().String()))) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - id1 := ids.ShortID(hashing.ComputeHash160Array([]byte(ip1.IP().String()))) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - ) - wg0.Add(1) - wg1.Add(1) - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id0, id) - wg0.Done() - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id1, id) - wg1.Done() - }, - } - - subnetSet := ids.Set{} - subnetSet.Add(testSubnetID) - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - subnetSet, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - subnetSet, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - - net0.Track(ip1.IP(), id1) - - wg0.Wait() - wg1.Wait() - peers := net0.(*network).peers - count := 0 - for _, peer := range peers.peersList { - if peer == nil { - continue - } - count++ - assert.True(t, peer.gotVersion.GetValue()) - assert.True(t, peer.trackedSubnets.Contains(testSubnetID)) - assert.True(t, peer.trackedSubnets.Contains(constants.PrimaryNetworkID)) - } - - assert.Greater(t, count, 0) - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) -} - -func TestPeerGossip(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - ip2 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 2, - ) - - id0 := certToID(cert0.Leaf) - id1 := certToID(cert1.Leaf) - id2 := certToID(cert2.Leaf) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - listener2 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller2 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - caller0.outbounds[ip2.IP().String()] = listener2 - caller1.outbounds[ip2.IP().String()] = listener2 - - vdrs := getDefaultManager() - beacons := validators.NewSet() - // id2 is a validator - err := vdrs.AddWeight(constants.PrimaryNetworkID, id2, math.MaxUint64) - assert.NoError(t, err) - - allPeers := ids.ShortSet{} - allPeers.Add(id0, id1, id2) - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - wg1P sync.WaitGroup - wg2 sync.WaitGroup - wg2P sync.WaitGroup - ) - wg0.Add(2) - wg1.Add(1) - wg1P.Add(2) - wg2.Add(1) - wg2P.Add(1) - - testSubnetContainerID := ids.GenerateTestID() - testPrimaryContainerID := ids.GenerateTestID() - allContainerIDs := []ids.ID{testSubnetContainerID, testPrimaryContainerID} - - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id0, id) - wg0.Done() - }, - PutF: func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte, onFinishedHandling func()) { - assert.Fail(t, "this should not receive any gossip") - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id1, id) - wg1.Done() - }, - PutF: func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte, onFinishedHandling func()) { - assert.Contains(t, allContainerIDs, containerID) - wg1P.Done() - }, - } - - metrics2 := prometheus.NewRegistry() - msgCreator2, err := message.NewCreator(metrics2, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler2 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id2, id) - wg2.Done() - }, - PutF: func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte, onFinishedHandling func()) { - // this one should not receive it - assert.NotEqual(t, testSubnetContainerID, containerID) - wg2P.Done() - }, - } - - subnetSet := ids.Set{} - subnetSet.Add(testSubnetID) - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - subnetSet, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - subnetSet, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net2, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, // tracks no subnet - tlsConfig2, - listener2, - caller2, - metrics2, - msgCreator2, - handler2, - ) - assert.NoError(t, err) - assert.NotNil(t, net2) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net2.Dispatch() - assert.Error(t, err) - }() - - // ip0 -> ip2 and ip0 -> ip1 connect - net0.Track(ip2.IP(), id2) - net0.Track(ip1.IP(), id1) - - wg0.Wait() - wg1.Wait() - wg2.Wait() - - gossipMsg0, err := msgCreator0.Put(ids.GenerateTestID(), constants.GossipMsgRequestID, testSubnetContainerID, []byte("test0")) - assert.NoError(t, err) - net0.Gossip(gossipMsg0, testSubnetID, false, 0, defaultGossipAcceptedFrontierSize) - - gossipMsg1, err := msgCreator0.Put(ids.GenerateTestID(), constants.GossipMsgRequestID, testPrimaryContainerID, []byte("test1")) - assert.NoError(t, err) - net0.Gossip(gossipMsg1, constants.PrimaryNetworkID, false, 0, defaultGossipAcceptedFrontierSize) - - wg1P.Wait() - wg2P.Wait() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net2.Close() - assert.NoError(t, err) -} - -func TestAppGossip(t *testing.T) { - initCerts(t) - - ip0 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - ip2 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 2, - ) - - id0 := certToID(cert0.Leaf) - id1 := certToID(cert1.Leaf) - id2 := certToID(cert2.Leaf) - - listener0 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller0 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - listener1 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller1 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 1, - }, - outbounds: make(map[string]*testListener), - } - listener2 := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller2 := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 2, - }, - outbounds: make(map[string]*testListener), - } - - caller0.outbounds[ip1.IP().String()] = listener1 - caller1.outbounds[ip0.IP().String()] = listener0 - caller0.outbounds[ip2.IP().String()] = listener2 - caller1.outbounds[ip2.IP().String()] = listener2 - - vdrs := getDefaultManager() - primaryVdrs := validators.NewSet() - _ = primaryVdrs.Set([]validators.Validator{validators.NewValidator(id2, math.MaxUint64)}) - // id2 is a validator - _ = vdrs.Set(constants.PrimaryNetworkID, primaryVdrs) - - beacons := validators.NewSet() - - allPeers := ids.ShortSet{} - allPeers.Add(id0, id1, id2) - - var ( - wg0 sync.WaitGroup - wg1 sync.WaitGroup - wg1P sync.WaitGroup - wg2 sync.WaitGroup - wg2P sync.WaitGroup - ) - wg0.Add(2) - wg1.Add(1) - wg1P.Add(1) - wg2.Add(1) - wg2P.Add(2) - - testAppGossipBytes := []byte("appgossip") - testAppGossipSpecificBytes := []byte("appgossipspecific") - metrics0 := prometheus.NewRegistry() - msgCreator0, err := message.NewCreator(metrics0, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler0 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id0, id) - wg0.Done() - }, - AppGossipF: func( - nodeID ids.ShortID, - chainID ids.ID, - appGossipBytes []byte, - onFinishedHandling func(), - ) { - assert.Fail(t, "this should not receive any App Gossips") - }, - } - - metrics1 := prometheus.NewRegistry() - msgCreator1, err := message.NewCreator(metrics1, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler1 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id1, id) - wg1.Done() - }, - AppGossipF: func( - nodeID ids.ShortID, - chainID ids.ID, - appGossipBytes []byte, - onFinishedHandling func(), - ) { - assert.Equal(t, testAppGossipBytes, appGossipBytes) - wg1P.Done() - }, - } - - metrics2 := prometheus.NewRegistry() - msgCreator2, err := message.NewCreator(metrics2, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler2 := &testHandler{ - ConnectedF: func(id ids.ShortID, nodeVersion version.Application) { - assert.NotEqual(t, id2, id) - wg2.Done() - }, - AppGossipF: func( - nodeID ids.ShortID, - chainID ids.ID, - appGossipBytes []byte, - onFinishedHandling func(), - ) { - assert.Contains(t, [][]byte{testAppGossipBytes, testAppGossipSpecificBytes}, appGossipBytes) - wg2P.Done() - }, - } - - net0, err := newTestNetwork( - id0, - ip0, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener0, - caller0, - metrics0, - msgCreator0, - handler0, - ) - assert.NoError(t, err) - assert.NotNil(t, net0) - - net1, err := newTestNetwork( - id1, - ip1, - defaultVersionManager, - vdrs, - beacons, - cert1.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig1, - listener1, - caller1, - metrics1, - msgCreator1, - handler1, - ) - assert.NoError(t, err) - assert.NotNil(t, net1) - - net2, err := newTestNetwork( - id2, - ip2, - defaultVersionManager, - vdrs, - beacons, - cert2.PrivateKey.(crypto.Signer), - ids.Set{}, // tracks no subnet - tlsConfig2, - listener2, - caller2, - metrics2, - msgCreator2, - handler2, - ) - assert.NoError(t, err) - assert.NotNil(t, net2) - - go func() { - err := net0.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net1.Dispatch() - assert.Error(t, err) - }() - go func() { - err := net2.Dispatch() - assert.Error(t, err) - }() - - // ip0 -> ip2 and ip0 -> ip1 connect - net0.Track(ip2.IP(), id2) - net0.Track(ip1.IP(), id1) - - wg0.Wait() - wg1.Wait() - wg2.Wait() - - chainID := ids.GenerateTestID() - msg1, err := msgCreator0.AppGossip(chainID, testAppGossipBytes) - assert.NoError(t, err) - net0.Gossip(msg1, constants.PrimaryNetworkID, false, defaultAppGossipValidatorSize, defaultAppGossipNonValidatorSize) - - specificNodeSet := ids.NewShortSet(1) - specificNodeSet.Add(id2) - msg2, err := msgCreator0.AppGossip(chainID, testAppGossipSpecificBytes) - assert.NoError(t, err) - net0.Send(msg2, specificNodeSet, constants.PrimaryNetworkID, false) - - wg1P.Wait() - wg2P.Wait() - - err = net0.Close() - assert.NoError(t, err) - - err = net1.Close() - assert.NoError(t, err) - - err = net2.Close() - assert.NoError(t, err) -} - -// Helper method for TestValidatorIPs -func createPeer(peerID ids.ShortID, peerIPDesc utils.IPDesc, peerVersion version.Application) *peer { - newPeer := peer{ - ip: peerIPDesc, - nodeID: peerID, - } - newPeer.finishedHandshake.SetValue(true) - newPeer.versionStruct.SetValue(peerVersion) - newPeer.sigAndTime.SetValue(signedPeerIP{ - ip: newPeer.ip, - time: uint64(0), - }) - - return &newPeer -} - -func addPeerToNetwork(targetNetwork *network, peerToAdd *peer, isValidator bool) { - targetNetwork.peers.add(peerToAdd) - - if isValidator { - _ = targetNetwork.config.Validators.AddWeight(constants.PrimaryNetworkID, peerToAdd.nodeID, 10) - } -} - -func clearPeersData(targetNetwork *network) { - targetNetwork.peers.reset() - targetNetwork.config.Validators = getDefaultManager() -} - -func isIPDescIn(targetIP utils.IPDesc, ipDescList []utils.IPCertDesc) bool { - for _, b := range ipDescList { - if b.IPDesc.Equal(targetIP) { - return true - } - } - return false -} - -func newDefaultNetwork( - id ids.ShortID, - ip utils.DynamicIPDesc, - vdrs validators.Manager, - beacons validators.Set, - tlsKey crypto.Signer, - subnetSet ids.Set, - tlsConfig *tls.Config, - listener net.Listener, - metrics *prometheus.Registry, - msgCreator message.Creator, - router router.Router, -) (Network, error) { - log := logging.NoLog{} - networkID := uint32(0) - benchlistManager := benchlist.NewManager(&benchlist.Config{}) - s := uptime.NewTestState() - - uptimeManager := uptime.NewManager(s) - - netConfig := newDefaultConfig() - netConfig.Namespace = "" - netConfig.MyNodeID = id - netConfig.MyIP = ip - netConfig.NetworkID = networkID - netConfig.Validators = vdrs - netConfig.Beacons = beacons - netConfig.TLSKey = tlsKey - netConfig.TLSConfig = tlsConfig - netConfig.PeerAliasTimeout = defaultAliasTimeout - netConfig.PeerListSize = defaultPeerListSize - netConfig.PeerListGossipSize = defaultGossipPeerListTo - netConfig.PeerListGossipFreq = defaultGossipPeerListFreq - netConfig.CompressionEnabled = true - netConfig.WhitelistedSubnets = subnetSet - netConfig.UptimeCalculator = uptimeManager - - n, err := NewNetwork(&netConfig, msgCreator, metrics, log, listener, router, benchlistManager) - if err != nil { - return nil, err - } - - return n, nil -} - -func newTestNetwork(id ids.ShortID, - ip utils.DynamicIPDesc, - versionCompatibility version.Compatibility, - vdrs validators.Manager, - beacons validators.Set, - tlsKey crypto.Signer, - subnetSet ids.Set, - tlsConfig *tls.Config, - listener net.Listener, - dialer dialer.Dialer, - metrics *prometheus.Registry, - msgCreator message.Creator, - router router.Router) (Network, error) { - n, err := newDefaultNetwork(id, ip, vdrs, beacons, tlsKey, subnetSet, tlsConfig, listener, metrics, msgCreator, router) - if err != nil { - return nil, err - } - netw := n.(*network) - netw.dialer = dialer - netw.versionCompatibility = versionCompatibility - netw.inboundMsgThrottler = defaultInboundMsgThrottler - netw.outboundMsgThrottler = defaultOutboundMsgThrottler - return netw, nil -} - -func newDefaultConfig() Config { - return Config{ - PeerListGossipConfig: PeerListGossipConfig{ - PeerListStakerGossipFraction: 2, - }, - DelayConfig: DelayConfig{ - MaxReconnectDelay: time.Hour, - InitialReconnectDelay: time.Second, - }, - TimeoutConfig: TimeoutConfig{ - GetVersionTimeout: 10 * time.Second, - PingPongTimeout: 30 * time.Second, - ReadHandshakeTimeout: 15 * time.Second, - }, - MaxClockDifference: time.Minute, - AllowPrivateIPs: true, - PingFrequency: constants.DefaultPingFrequency, - UptimeMetricFreq: 30 * time.Second, + for _, net := range networks { + net.StartClose() } + wg.Wait() } diff --git a/network/peer.go b/network/peer.go deleted file mode 100644 index 617594bfa090..000000000000 --- a/network/peer.go +++ /dev/null @@ -1,990 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package network - -import ( - "bufio" - "bytes" - "crypto/x509" - "encoding/binary" - "io" - "math" - "net" - "sync" - "sync/atomic" - "time" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/message" - "github.com/ava-labs/avalanchego/utils" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/formatting" - "github.com/ava-labs/avalanchego/utils/hashing" - "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/version" -) - -// The signature of a peer's certificate on the byte representation -// of the peer's IP and time, and the time, in Unix seconds. -type signedPeerIP struct { - ip utils.IPDesc - time uint64 - signature []byte -} - -// alias is a secondary IP address where a peer -// was reached -type alias struct { - // ip where peer was reached - ip utils.IPDesc - - // expiry is network time when the ip should be released - expiry time.Time -} - -type peer struct { - net *network // network this peer is part of - - // True if this peer has sent us a valid Version message and - // is running a compatible version. - // Only modified on the connection's reader routine. - gotVersion utils.AtomicBool - - // True if this peer has sent us a valid PeerList message. - // Only modified on the connection's reader routine. - gotPeerList utils.AtomicBool - - // only send the version to this peer on handling a getVersion message if - // a version hasn't already been sent. - versionSent utils.AtomicBool - - // only send the peerlist to this peer on handling a getPeerlist message if - // a peerlist hasn't already been sent. - peerListSent utils.AtomicBool - - // True if the peer: - // * Has sent us a Version message - // * Has sent us a PeerList message - // * Is a compatible version - // Only modified on the connection's reader routine. - finishedHandshake utils.AtomicBool - - // only close the peer once - once sync.Once - - // if the close function has been called. - closed utils.AtomicBool - - // queue of messages to be sent to this peer - sendQueue []message.OutboundMessage - - // Signalled when a message is added to [sendQueue], - // and when [p.closed] is set to true. - // [sendQueueCond.L] must be held when using [sendQueue]. - sendQueueCond *sync.Cond - - // ip may or may not be set when the peer is first started. is only modified - // on the connection's reader routine. - ip utils.IPDesc - - // ipLock must be held when accessing [ip]. - ipLock sync.RWMutex - - // aliases is a list of IPs other than [ip] that we have connected to - // this peer at. - aliases []alias - - // aliasTimer triggers the release of expired records from [aliases]. - aliasTimer *timer.Timer - - // aliasLock must be held when accessing [aliases] or [aliasTimer]. - aliasLock sync.Mutex - - // node ID of this peer. - nodeID ids.ShortID - - // the connection object that is used to read/write messages from - conn net.Conn - - // Version that this peer reported during the handshake. - // Set when we process the Version message from this peer. - versionStruct, versionStr utils.AtomicInterface - - // Unix time of the last message sent and received respectively - // Must only be accessed atomically - lastSent, lastReceived int64 - - tickerCloser chan struct{} - - // ticker processes - tickerOnce sync.Once - - // [cert] is this peer's certificate (specifically the leaf of the certificate chain they provided) - cert *x509.Certificate - - // sigAndTime contains a struct of type sigAndTime. - // The signature is [cert]'s signature on the peer's IP, concatenated with - // the peer's local time when it sent a Version. - // The time in [sigAndTime] is the one mentioned above. - sigAndTime utils.AtomicInterface - - // trackedSubnets hold subnetIDs that this peer is interested in. - trackedSubnets ids.Set - - // observedUptime is the uptime of this node in peer's point of view - observedUptime uint8 -} - -// newPeer returns a properly initialized *peer. -func newPeer(net *network, conn net.Conn, ip utils.IPDesc) *peer { - p := &peer{ - sendQueueCond: sync.NewCond(&sync.Mutex{}), - net: net, - conn: conn, - ip: ip, - tickerCloser: make(chan struct{}), - } - p.aliasTimer = timer.NewTimer(p.releaseExpiredAliases) - p.trackedSubnets.Add(constants.PrimaryNetworkID) - return p -} - -// assume the [stateLock] is held -func (p *peer) Start() { - // Register this node with the inbound message throttler. - // Note: we must call [p.net.inboundMsgThrottler.RemoveNode(p.nodeID)] - // after we stop reading messages from [p.nodeID]. - // This happens in [p.Close]. - // Failure to call RemoveNode will cause a memory leak. - p.net.inboundMsgThrottler.AddNode(p.nodeID) - go func() { - // Make sure that the version is the first message sent - p.sendVersion() - - go p.ReadMessages() - go p.WriteMessages() - }() -} - -func (p *peer) StartTicker() { - go p.requestFinishHandshake() - go p.sendPings() - go p.monitorAliases() -} - -func (p *peer) sendPings() { - sendPingsTicker := time.NewTicker(p.net.config.PingFrequency) - defer sendPingsTicker.Stop() - - for { - select { - case <-sendPingsTicker.C: - closed := p.closed.GetValue() - - if closed { - return - } - - p.sendPing() - case <-p.tickerCloser: - return - } - } -} - -// request missing handshake messages from the peer -func (p *peer) requestFinishHandshake() { - finishHandshakeTicker := time.NewTicker(p.net.config.GetVersionTimeout) - defer finishHandshakeTicker.Stop() - - for { - select { - case <-finishHandshakeTicker.C: - if p.finishedHandshake.GetValue() { - return - } - if p.closed.GetValue() { - return - } - if !p.gotVersion.GetValue() { - p.sendGetVersion() - } - if !p.gotPeerList.GetValue() { - p.sendGetPeerList() - } - case <-p.tickerCloser: - return - } - } -} - -// monitorAliases periodically attempts -// to release timed out alias IPs of the -// peer. -// -// monitorAliases will acquire [stateLock] -// when an alias is released. -func (p *peer) monitorAliases() { - go func() { - <-p.tickerCloser - p.aliasTimer.Stop() - }() - - p.aliasTimer.Dispatch() -} - -// Read and handle messages from this peer. -// When this method returns, the connection is closed. -func (p *peer) ReadMessages() { - defer p.Close() - - // Continuously read and handle messages from this peer. - reader := bufio.NewReader(p.conn) - msgLenBytes := make([]byte, wrappers.IntLen) - for { - // Time out and close connection if we can't read message length - if err := p.conn.SetReadDeadline(p.nextTimeout()); err != nil { - p.net.log.Verbo("error setting the connection read timeout on %s%s at %s %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - return - } - - // Read the message length - if _, err := io.ReadFull(reader, msgLenBytes); err != nil { - p.net.log.Verbo("error reading from %s%s at %s: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - return - } - - // Parse the message length - msgLen := binary.BigEndian.Uint32(msgLenBytes) - - // Make sure the message length is valid. - if int64(msgLen) > int64(constants.DefaultMaxMessageSize) { - p.net.log.Verbo("too large message length %d from %s%s at %s", msgLen, constants.NodeIDPrefix, p.nodeID, p.getIP()) - return - } - - // Wait until the throttler says we can proceed to read the message. - // Note that when we are done handling this message, or give up - // trying to read it, we must call [p.net.msgThrottler.Release] - // to give back the bytes used by this message. - p.net.inboundMsgThrottler.Acquire(uint64(msgLen), p.nodeID) - - // Invariant: When done processing this message, onFinishedHandling() is called. - // If this is not honored, the message throttler will leak until no new messages can be read. - // You can look at message throttler metrics to verify that there is no leak. - onFinishedHandling := func() { p.net.inboundMsgThrottler.Release(uint64(msgLen), p.nodeID) } - - // Time out and close connection if we can't read message - if err := p.conn.SetReadDeadline(p.nextTimeout()); err != nil { - p.net.log.Verbo("error setting the connection read timeout on %s%s at %s %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - onFinishedHandling() - return - } - - // Read the message - msgBytes := make([]byte, msgLen) - if _, err := io.ReadFull(reader, msgBytes); err != nil { - p.net.log.Verbo("error reading from %s%s at %s: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - onFinishedHandling() - return - } - - p.net.log.Verbo("parsing message from %s%s at %s:\n%s", constants.NodeIDPrefix, p.nodeID, p.getIP(), formatting.DumpBytes(msgBytes)) - - // Parse the message - msg, err := p.net.mc.Parse(msgBytes, p.nodeID, onFinishedHandling) - if err != nil { - p.net.log.Verbo("failed to parse message from %s%s at %s:\n%s\n%s", constants.NodeIDPrefix, p.nodeID, p.getIP(), formatting.DumpBytes(msgBytes), err) - // Couldn't parse the message. Read the next one. - onFinishedHandling() - p.net.metrics.failedToParse.Inc() - continue - } - - // Handle the message. Note that when we are done handling - // this message, we must call [p.net.msgThrottler.Release] - // to release the bytes used by this message. See MsgThrottler. - p.handle(msg, float64(len(msgBytes))) - } -} - -// attempt to write messages to the peer -func (p *peer) WriteMessages() { - defer p.Close() - - var reader bytes.Reader - writer := bufio.NewWriter(p.conn) - for { // When this loop exits, p.sendQueueCond.L is unlocked - p.sendQueueCond.L.Lock() - for { - if p.closed.GetValue() { - p.sendQueueCond.L.Unlock() - return - } - if len(p.sendQueue) > 0 { - // There is a message to send - break - } - // Wait until there is a message to send - p.sendQueueCond.Wait() - } - msg := p.sendQueue[0] - p.sendQueue = p.sendQueue[1:] - p.sendQueueCond.L.Unlock() - - msgLen := uint32(len(msg.Bytes())) - p.net.outboundMsgThrottler.Release(msg, p.nodeID) - p.net.log.Verbo("sending message to %s%s at %s:\n%s", constants.NodeIDPrefix, p.nodeID, p.getIP(), formatting.DumpBytes(msg.Bytes())) - msgb := [wrappers.IntLen]byte{} - binary.BigEndian.PutUint32(msgb[:], msgLen) - for _, byteSlice := range [2][]byte{msgb[:], msg.Bytes()} { - reader.Reset(byteSlice) - if err := p.conn.SetWriteDeadline(p.nextTimeout()); err != nil { - p.net.log.Verbo("error setting write deadline to %s%s at %s due to: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - msg.DecRef() - return - } - if _, err := io.CopyN(writer, &reader, int64(len((byteSlice)))); err != nil { - p.net.log.Verbo("error writing to %s%s at %s due to: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - msg.DecRef() - return - } - } - p.tickerOnce.Do(p.StartTicker) - // Make sure the peer got the entire message - if err := writer.Flush(); err != nil { - p.net.log.Verbo("couldn't flush writer to %s%s at %s: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - msg.DecRef() - return - } - - now := p.net.clock.Time().Unix() - atomic.StoreInt64(&p.lastSent, now) - atomic.StoreInt64(&p.net.lastMsgSentTime, now) - - msg.DecRef() - } -} - -// send assumes that the [stateLock] is not held. -func (p *peer) Send(msg message.OutboundMessage) bool { - // Acquire space on the outbound message queue, or drop [msg] if we can't - if !p.net.outboundMsgThrottler.Acquire(msg, p.nodeID) { - p.net.log.Debug("dropping %s message to %s%s at %s due to rate-limiting", msg.Op(), constants.NodeIDPrefix, p.nodeID, p.getIP()) - return false - } - - // Invariant: must call p.net.outboundMsgThrottler.Release(msg, p.nodeID) - // when done sending [msg] or when we give up sending [msg] - - p.sendQueueCond.L.Lock() - defer p.sendQueueCond.L.Unlock() - - if p.closed.GetValue() { - p.net.log.Debug("dropping message to %s%s at %s due to a closed connection", constants.NodeIDPrefix, p.nodeID, p.getIP()) - p.net.outboundMsgThrottler.Release(msg, p.nodeID) - return false - } - - p.sendQueue = append(p.sendQueue, msg) - p.sendQueueCond.Signal() - return true -} - -// assumes the [stateLock] is not held -func (p *peer) handle(msg message.InboundMessage, msgLen float64) { - now := p.net.clock.Time() - atomic.StoreInt64(&p.lastReceived, now.Unix()) - atomic.StoreInt64(&p.net.lastMsgReceivedTime, now.Unix()) - - op := msg.Op() - msgMetrics := p.net.metrics.messageMetrics[op] - if msgMetrics == nil { - p.net.log.Error("dropping an unknown message from %s%s at %s with op %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), op) - msg.OnFinishedHandling() - return - } - msgMetrics.numReceived.Inc() - msgMetrics.receivedBytes.Add(msgLen) - // assume that if [saved] == 0, [msg] wasn't compressed - if saved := msg.BytesSavedCompression(); saved != 0 { - msgMetrics.savedReceivedBytes.Observe(float64(saved)) - } - - switch op { // Network-related message types - case message.Version: - p.handleVersion(msg) - msg.OnFinishedHandling() - return - case message.GetVersion: - p.handleGetVersion(msg) - msg.OnFinishedHandling() - return - case message.Ping: - p.handlePing(msg) - msg.OnFinishedHandling() - return - case message.Pong: - p.handlePong(msg) - msg.OnFinishedHandling() - return - case message.GetPeerList: - p.handleGetPeerList(msg) - msg.OnFinishedHandling() - return - case message.PeerList: - p.handlePeerList(msg) - msg.OnFinishedHandling() - return - } - if !p.finishedHandshake.GetValue() { - p.net.log.Debug("dropping %s from %s%s at %s because handshake isn't finished", op, constants.NodeIDPrefix, p.nodeID, p.getIP()) - - // attempt to finish the handshake - if !p.gotVersion.GetValue() { - p.sendGetVersion() - } - if !p.gotPeerList.GetValue() { - p.sendGetPeerList() - } - msg.OnFinishedHandling() - return - } - - // Consensus and app-level messages - p.net.router.HandleInbound(msg) -} - -// assumes the [stateLock] is not held -func (p *peer) Close() { p.once.Do(p.close) } - -// assumes only [peer.Close] calls this. -// By the time this message returns, [p] has been removed from [p.net.peers] -func (p *peer) close() { - // If the connection is closing, we can immediately cancel the ticker - // goroutines. - close(p.tickerCloser) - - p.closed.SetValue(true) - - if err := p.conn.Close(); err != nil { - p.net.log.Debug("closing connection to %s%s at %s resulted in an error: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - } - - // Remove this node from the throttler. - p.net.inboundMsgThrottler.RemoveNode(p.nodeID) - - p.sendQueueCond.L.Lock() - // Release the bytes of the unsent messages to the outbound message throttler - for i := 0; i < len(p.sendQueue); i++ { - msg := p.sendQueue[i] - p.net.outboundMsgThrottler.Release(msg, p.nodeID) - msg.DecRef() - } - p.sendQueue = nil - p.sendQueueCond.L.Unlock() - // Per [p.sendQueueCond]'s spec, it is signalled when [p.closed] is set to true - // so that we exit the WriteMessages goroutine. - // Since [p.closed] is now true, nothing else will be put on [p.sendQueue] - p.sendQueueCond.Signal() - p.net.disconnected(p) -} - -// assumes the [stateLock] is not held -func (p *peer) sendGetVersion() { - msg, err := p.net.mc.GetVersion() - p.net.log.AssertNoError(err) - p.net.send(msg, []*peer{p}, false) -} - -// assumes the [stateLock] is not held -func (p *peer) sendVersion() { - p.net.stateLock.RLock() - myIP := p.net.currentIP.IP() - myVersionTime, myVersionSig, err := p.net.getVersion(myIP) - if err != nil { - p.net.stateLock.RUnlock() - return - } - whitelistedSubnets := p.net.config.WhitelistedSubnets - msg, err := p.net.mc.Version( - p.net.config.NetworkID, - p.net.dummyNodeID, - p.net.clock.Unix(), - myIP, - p.net.versionCompatibility.Version().String(), - myVersionTime, - myVersionSig, - whitelistedSubnets.List(), - ) - p.net.stateLock.RUnlock() - p.net.log.AssertNoError(err) - - p.net.send(msg, []*peer{p}, false) -} - -// assumes the [stateLock] is not held -func (p *peer) sendGetPeerList() { - msg, err := p.net.mc.GetPeerList() - p.net.log.AssertNoError(err) - - p.net.send(msg, []*peer{p}, false) -} - -// assumes the stateLock is not held -func (p *peer) sendPeerList() { - peers, err := p.net.validatorIPs() - if err != nil { - return - } - - msg, err := p.net.mc.PeerList(peers) - if err != nil { - p.net.log.Warn("failed to send PeerList to %s%s at %s: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - return - } - - p.net.send(msg, []*peer{p}, false) -} - -// assumes the [stateLock] is not held -func (p *peer) sendPing() { - msg, err := p.net.mc.Ping() - p.net.log.AssertNoError(err) - - p.net.send(msg, []*peer{p}, false) -} - -// assumes the [stateLock] is not held -func (p *peer) sendPong() { - uptimePercent, err := p.net.config.UptimeCalculator.CalculateUptimePercent(p.nodeID) - if err != nil { - uptimePercent = 0 - } - // let's round down and send percentage (0-100) instead of float - // with this way we can pack it into a single byte - flooredPercentage := math.Floor(uptimePercent * 100) - percentage := uint8(flooredPercentage) - msg, err := p.net.mc.Pong(percentage) - p.net.log.AssertNoError(err) - - p.net.send(msg, []*peer{p}, false) -} - -// assumes the [stateLock] is not held -func (p *peer) handleGetVersion(_ message.InboundMessage) { - if !p.versionSent.GetValue() { - p.sendVersion() - } -} - -// assumes the [stateLock] is not held -func (p *peer) handleVersion(msg message.InboundMessage) { - switch { - case p.gotVersion.GetValue(): - p.net.log.Verbo("dropping duplicated version message from %s%s at %s", constants.NodeIDPrefix, p.nodeID, p.getIP()) - return - case msg.Get(message.NodeID).(uint32) == p.net.dummyNodeID: - p.net.log.Debug("peer at %s has same node ID as me", p.getIP()) - p.discardMyIP() - return - case msg.Get(message.NetworkID).(uint32) != p.net.config.NetworkID: - p.net.log.Debug( - "network ID of %s%s at %s (%d) doesn't match our's (%d)", - constants.NodeIDPrefix, p.nodeID, p.getIP(), msg.Get(message.NetworkID).(uint32), p.net.config.NetworkID, - ) - p.discardIP() - return - case p.closed.GetValue(): - return - } - myTime := float64(p.net.clock.Unix()) - peerTime := float64(msg.Get(message.MyTime).(uint64)) - if math.Abs(peerTime-myTime) > p.net.config.MaxClockDifference.Seconds() { - if p.net.config.Beacons.Contains(p.nodeID) { - p.net.log.Warn( - "beacon %s%s at %s reports time (%d) that is too far out of sync with our's (%d)", - constants.NodeIDPrefix, p.nodeID, p.getIP(), uint64(peerTime), uint64(myTime), - ) - } else { - p.net.log.Debug( - "peer %s%s at %s reports time (%d) that is too far out of sync with our's (%d)", - constants.NodeIDPrefix, p.nodeID, p.getIP(), uint64(peerTime), uint64(myTime), - ) - } - p.discardIP() - return - } - - peerVersionStr := msg.Get(message.VersionStr).(string) - peerVersion, err := p.net.parser.Parse(peerVersionStr) - if err != nil { - p.net.log.Debug("version of %s%s at %s could not be parsed: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - p.discardIP() - p.net.metrics.failedToParse.Inc() - return - } - - if p.net.versionCompatibility.Version().Before(peerVersion) { - if p.net.config.Beacons.Contains(p.nodeID) { - p.net.log.Info( - "beacon %s%s at %s attempting to connect with newer version %s. You may want to update your client", - constants.NodeIDPrefix, p.nodeID, p.getIP(), peerVersion, - ) - } else { - p.net.log.Debug( - "peer %s%s at %s attempting to connect with newer version %s. You may want to update your client", - constants.NodeIDPrefix, p.nodeID, p.getIP(), peerVersion, - ) - } - } - - if err := p.net.versionCompatibility.Compatible(peerVersion); err != nil { - p.net.log.Verbo("peer %s%s at %s version (%s) not compatible: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), peerVersion, err) - p.discardIP() - return - } - - peerIP := msg.Get(message.IP).(utils.IPDesc) - - versionTime := msg.Get(message.VersionTime).(uint64) - p.net.stateLock.RLock() - latestPeerIP := p.net.latestPeerIP[p.nodeID] - p.net.stateLock.RUnlock() - if latestPeerIP.time > versionTime { - p.discardIP() - return - } - if float64(versionTime)-myTime > p.net.config.MaxClockDifference.Seconds() { - p.net.log.Debug( - "peer %s%s at %s attempting to connect with version timestamp (%d) too far in the future", - constants.NodeIDPrefix, p.nodeID, p.getIP(), latestPeerIP.time, - ) - p.discardIP() - return - } - - // handle subnet IDs - subnetIDsBytes := msg.Get(message.TrackedSubnets).([][]byte) - for _, subnetIDBytes := range subnetIDsBytes { - subnetID, err := ids.ToID(subnetIDBytes) - if err != nil { - p.net.log.Debug("tracked subnet of %s%s at %s could not be parsed: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - p.discardIP() - return - } - // add only if we also track this subnet - if p.net.config.WhitelistedSubnets.Contains(subnetID) { - p.trackedSubnets.Add(subnetID) - } - } - - sig := msg.Get(message.SigBytes).([]byte) - signed := ipAndTimeBytes(peerIP, versionTime) - if err := p.cert.CheckSignature(p.cert.SignatureAlgorithm, signed, sig); err != nil { - p.net.log.Debug("signature verification failed for %s%s at %s: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), err) - p.discardIP() - return - } - - signedPeerIP := signedPeerIP{ - ip: peerIP, - time: versionTime, - signature: sig, - } - - p.net.stateLock.Lock() - p.net.latestPeerIP[p.nodeID] = signedPeerIP - p.net.stateLock.Unlock() - - p.sigAndTime.SetValue(signedPeerIP) - - if ip := p.getIP(); ip.IsZero() { - addr := p.conn.RemoteAddr() - localPeerIP, err := utils.ToIPDesc(addr.String()) - if err == nil { - // If we have no clue what the peer's IP is, we can't perform any - // verification - if peerIP.IP.Equal(localPeerIP.IP) { - // if the IPs match, add this ip:port pair to be tracked - p.setIP(peerIP) - } - } - } - - p.sendPeerList() - - p.versionStruct.SetValue(peerVersion) - p.versionStr.SetValue(peerVersion.String()) - p.gotVersion.SetValue(true) - - p.tryMarkFinishedHandshake() -} - -// assumes the [stateLock] is not held -func (p *peer) handleGetPeerList(_ message.InboundMessage) { - if p.gotVersion.GetValue() && !p.peerListSent.GetValue() { - p.sendPeerList() - } -} - -func (p *peer) trackSignedPeer(peer utils.IPCertDesc) { - p.net.stateLock.Lock() - defer p.net.stateLock.Unlock() - - switch { - case peer.IPDesc.Equal(p.net.currentIP.IP()): - return - case peer.IPDesc.IsZero(): - return - case !p.net.config.AllowPrivateIPs && peer.IPDesc.IsPrivate(): - return - } - - if float64(peer.Time)-float64(p.net.clock.Unix()) > p.net.config.MaxClockDifference.Seconds() { - p.net.log.Debug("ignoring gossiped peer with version timestamp (%d) too far in the future", peer.Time) - return - } - - nodeID := certToID(peer.Cert) - if !p.net.config.Validators.Contains(constants.PrimaryNetworkID, nodeID) && !p.net.config.Beacons.Contains(nodeID) { - p.net.log.Verbo( - "not peering to %s at %s because they are not a validator or beacon", - nodeID.PrefixedString(constants.NodeIDPrefix), peer.IPDesc, - ) - return - } - - // Am I already peered to them? (safe because [p.net.stateLock] is held) - - if foundPeer, ok := p.net.peers.getByID(nodeID); ok && !foundPeer.closed.GetValue() { - p.net.log.Verbo( - "not peering to %s because we are already connected to %s", - peer.IPDesc, nodeID.PrefixedString(constants.NodeIDPrefix), - ) - return - } - - if p.net.latestPeerIP[nodeID].time > peer.Time { - p.net.log.Verbo( - "not peering to %s at %s: the given timestamp (%d) < latest (%d)", - nodeID.PrefixedString(constants.NodeIDPrefix), peer.IPDesc, peer.Time, p.net.latestPeerIP[nodeID].time, - ) - return - } - - signed := ipAndTimeBytes(peer.IPDesc, peer.Time) - err := peer.Cert.CheckSignature(peer.Cert.SignatureAlgorithm, signed, peer.Signature) - if err != nil { - p.net.log.Debug( - "signature verification failed for %s at %s: %s", - nodeID.PrefixedString(constants.NodeIDPrefix), peer.IPDesc, err, - ) - return - } - p.net.latestPeerIP[nodeID] = signedPeerIP{ - ip: peer.IPDesc, - time: peer.Time, - } - - p.net.track(peer.IPDesc, nodeID) -} - -// assumes the [stateLock] is not held -func (p *peer) handlePeerList(msg message.InboundMessage) { - p.gotPeerList.SetValue(true) - p.tryMarkFinishedHandshake() - - ips := msg.Get(message.SignedPeers).([]utils.IPCertDesc) - for _, ip := range ips { - p.trackSignedPeer(ip) - } -} - -// assumes the [stateLock] is not held -func (p *peer) handlePing(_ message.InboundMessage) { - p.sendPong() -} - -// assumes the [stateLock] is not held -func (p *peer) handlePong(msg message.InboundMessage) { - if !p.net.shouldHoldConnection(p.nodeID) { - p.net.log.Debug("disconnecting from peer %s%s at %s because the peer is not a validator", constants.NodeIDPrefix, p.nodeID, p.getIP()) - p.discardIP() - return - } - - if !p.finishedHandshake.GetValue() { - // If the handshake isn't finished - do nothing - return - } - - peerVersion := p.versionStruct.GetValue().(version.Application) - if err := p.net.versionCompatibility.Compatible(peerVersion); err != nil { - p.net.log.Debug("disconnecting from peer %s%s at %s version (%s) not compatible: %s", constants.NodeIDPrefix, p.nodeID, p.getIP(), peerVersion, err) - p.discardIP() - } - - // if the peer or this node is not a validator, we don't need their uptime. - if p.net.config.Validators.Contains(constants.PrimaryNetworkID, p.nodeID) && - p.net.config.Validators.Contains(constants.PrimaryNetworkID, p.net.config.MyNodeID) { - uptime := msg.Get(message.Uptime).(uint8) - if uptime <= 100 { - p.observedUptime = uptime // [0, 100] percentage - } - } -} - -// assumes the [stateLock] is held -func (p *peer) tryMarkFinishedHandshake() { - if !p.finishedHandshake.GetValue() && // not already marked as finished with handshake - p.gotVersion.GetValue() && // not waiting for Version - p.gotPeerList.GetValue() && // not waiting for PeerList - !p.closed.GetValue() { // not already disconnected - p.net.connected(p) - } -} - -func (p *peer) discardIP() { - // By clearing the IP, we will not attempt to reconnect to this peer - if ip := p.getIP(); !ip.IsZero() { - p.setIP(utils.IPDesc{}) - - ipStr := ip.String() - - p.net.stateLock.Lock() - // Make sure the IP isn't marked as connected or disconnected to allow - // future connection attempts if the IP is heard from a peerlist gossip - delete(p.net.disconnectedIPs, ipStr) - delete(p.net.connectedIPs, ipStr) - p.net.stateLock.Unlock() - } - p.Close() -} - -func (p *peer) discardMyIP() { - // By clearing the IP, we will not attempt to reconnect to this peer - if ip := p.getIP(); !ip.IsZero() { - p.setIP(utils.IPDesc{}) - - str := ip.String() - - p.net.stateLock.Lock() - p.net.myIPs[str] = struct{}{} - delete(p.net.disconnectedIPs, str) - p.net.stateLock.Unlock() - } - p.Close() -} - -func (p *peer) setIP(ip utils.IPDesc) { - p.ipLock.Lock() - defer p.ipLock.Unlock() - p.ip = ip -} - -func (p *peer) getIP() utils.IPDesc { - p.ipLock.RLock() - defer p.ipLock.RUnlock() - return p.ip -} - -// addAlias marks that we have found another -// IP that we can connect to this peer at. -// -// assumes [stateLock] is held -func (p *peer) addAlias(ip utils.IPDesc) { - p.aliasLock.Lock() - defer p.aliasLock.Unlock() - - p.net.peerAliasIPs[ip.String()] = struct{}{} - p.aliases = append(p.aliases, alias{ - ip: ip, - expiry: p.net.clock.Time().Add(p.net.config.PeerAliasTimeout), - }) - - // Set the [aliasTimer] if this ip is the first alias we put - // in [aliases]. - if len(p.aliases) == 1 { - p.aliasTimer.SetTimeoutIn(p.net.config.PeerAliasTimeout) - } -} - -// releaseNextAlias returns the next released alias or nil if none was released. -// If none was released, then this will schedule the next time to remove an -// alias. -// -// assumes [stateLock] is held -func (p *peer) releaseNextAlias(now time.Time) *alias { - p.aliasLock.Lock() - defer p.aliasLock.Unlock() - - if len(p.aliases) == 0 { - return nil - } - - next := p.aliases[0] - if timeUntilExpiry := next.expiry.Sub(now); timeUntilExpiry > 0 { - p.aliasTimer.SetTimeoutIn(timeUntilExpiry) - return nil - } - p.aliases = p.aliases[1:] - - p.net.log.Verbo("released alias %s for peer %s%s", next.ip, constants.NodeIDPrefix, p.nodeID) - return &next -} - -// releaseExpiredAliases frees expired IP aliases. If there is an IP pending -// expiration, then the expiration is scheduled. -// -// assumes [stateLock] is not held -func (p *peer) releaseExpiredAliases() { - currentTime := p.net.clock.Time() - for { - next := p.releaseNextAlias(currentTime) - if next == nil { - return - } - - // We should always release [aliasLock] before attempting - // to acquire the [stateLock] to avoid deadlocking on addAlias. - p.net.stateLock.Lock() - delete(p.net.peerAliasIPs, next.ip.String()) - p.net.stateLock.Unlock() - } -} - -// releaseAllAliases frees all alias IPs. -// -// assumes [stateLock] is held and that [aliasTimer] -// has been stopped -func (p *peer) releaseAllAliases() { - p.aliasLock.Lock() - defer p.aliasLock.Unlock() - - for _, alias := range p.aliases { - delete(p.net.peerAliasIPs, alias.ip.String()) - - p.net.log.Verbo("released alias %s for peer %s%s", alias.ip, constants.NodeIDPrefix, p.nodeID) - } - p.aliases = nil -} - -func (p *peer) nextTimeout() time.Time { - return p.net.clock.Time().Add(p.net.config.PingPongTimeout) -} - -func ipAndTimeBytes(ip utils.IPDesc, timestamp uint64) []byte { - p := wrappers.Packer{ - Bytes: make([]byte, wrappers.IPLen+wrappers.LongLen), - } - p.PackIP(ip) - p.PackLong(timestamp) - return p.Bytes -} - -func ipAndTimeHash(ip utils.IPDesc, timestamp uint64) []byte { - return hashing.ComputeHash256(ipAndTimeBytes(ip, timestamp)) -} diff --git a/network/peer/config.go b/network/peer/config.go new file mode 100644 index 000000000000..c316f0c6e43e --- /dev/null +++ b/network/peer/config.go @@ -0,0 +1,40 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/network/throttling" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/version" +) + +type Config struct { + Clock mockable.Clock + Metrics *Metrics + MessageCreator message.Creator + Log logging.Logger + InboundMsgThrottler throttling.InboundMsgThrottler + OutboundMsgThrottler throttling.OutboundMsgThrottler + Network Network + Router router.InboundHandler + VersionCompatibility version.Compatibility + VersionParser version.ApplicationParser + MySubnets ids.Set + Beacons validators.Set + NetworkID uint32 + PingFrequency time.Duration + PongTimeout time.Duration + MaxClockDifference time.Duration + + // Unix time of the last message sent and received respectively + // Must only be accessed atomically + LastSent, LastReceived int64 +} diff --git a/network/peer/example_test.go b/network/peer/example_test.go new file mode 100644 index 000000000000..6c2900cba06a --- /dev/null +++ b/network/peer/example_test.go @@ -0,0 +1,46 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" +) + +func ExampleStartTestPeer() { + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, 15*time.Second) + defer cancel() + + peerIP := utils.IPDesc{ + IP: net.IPv6loopback, + Port: 9651, + } + peer, err := StartTestPeer( + ctx, + peerIP, + constants.LocalID, + router.InboundHandlerFunc(func(msg message.InboundMessage) { + fmt.Printf("handling %s\n", msg.Op()) + }), + ) + if err != nil { + panic(err) + } + + // Send messages here with [peer.Send]. + + peer.StartClose() + err = peer.AwaitClosed(ctx) + if err != nil { + panic(err) + } +} diff --git a/network/peer_info.go b/network/peer/info.go similarity index 87% rename from network/peer_info.go rename to network/peer/info.go index 37b6a9283742..fda38b6a2828 100644 --- a/network/peer_info.go +++ b/network/peer/info.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package network +package peer import ( "time" @@ -10,14 +10,13 @@ import ( "github.com/ava-labs/avalanchego/utils/json" ) -type PeerInfo struct { +type Info struct { IP string `json:"ip"` PublicIP string `json:"publicIP,omitempty"` ID string `json:"nodeID"` Version string `json:"version"` LastSent time.Time `json:"lastSent"` LastReceived time.Time `json:"lastReceived"` - Benched []ids.ID `json:"benched"` ObservedUptime json.Uint8 `json:"observedUptime"` TrackedSubnets []ids.ID `json:"trackedSubnets"` } diff --git a/network/peer/ip.go b/network/peer/ip.go new file mode 100644 index 000000000000..c102f8326ff5 --- /dev/null +++ b/network/peer/ip.go @@ -0,0 +1,58 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "crypto" + "crypto/rand" + "crypto/x509" + + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/utils/wrappers" +) + +// UnsignedIP is used for a validator to claim an IP. The [Timestamp] is used to +// ensure that the most updated IP claim is tracked by peers for a given +// validator. +type UnsignedIP struct { + IP utils.IPDesc + Timestamp uint64 +} + +// Sign this IP with the provided signer and return the signed IP. +func (ip *UnsignedIP) Sign(signer crypto.Signer) (*SignedIP, error) { + sig, err := signer.Sign( + rand.Reader, + hashing.ComputeHash256(ip.bytes()), + crypto.SHA256, + ) + return &SignedIP{ + IP: *ip, + Signature: sig, + }, err +} + +func (ip *UnsignedIP) bytes() []byte { + p := wrappers.Packer{ + Bytes: make([]byte, wrappers.IPLen+wrappers.LongLen), + } + p.PackIP(ip.IP) + p.PackLong(ip.Timestamp) + return p.Bytes +} + +// SignedIP is a wrapper of an UnsignedIP with the signature from a signer. +type SignedIP struct { + IP UnsignedIP + Signature []byte +} + +func (ip *SignedIP) verify(cert *x509.Certificate) error { + return cert.CheckSignature( + cert.SignatureAlgorithm, + ip.IP.bytes(), + ip.Signature, + ) +} diff --git a/network/peer/metrics.go b/network/peer/metrics.go new file mode 100644 index 000000000000..fc3bba63385e --- /dev/null +++ b/network/peer/metrics.go @@ -0,0 +1,178 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/metric" + "github.com/ava-labs/avalanchego/utils/wrappers" +) + +type MessageMetrics struct { + ReceivedBytes, SentBytes, NumSent, NumFailed, NumReceived prometheus.Counter + SavedReceivedBytes, SavedSentBytes metric.Averager +} + +func NewMessageMetrics( + op message.Op, + namespace string, + metrics prometheus.Registerer, + errs *wrappers.Errs, +) *MessageMetrics { + msg := &MessageMetrics{ + NumSent: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: fmt.Sprintf("%s_sent", op), + Help: fmt.Sprintf("Number of %s messages sent over the network", op), + }), + NumFailed: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: fmt.Sprintf("%s_failed", op), + Help: fmt.Sprintf("Number of %s messages that failed to be sent over the network", op), + }), + NumReceived: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: fmt.Sprintf("%s_received", op), + Help: fmt.Sprintf("Number of %s messages received from the network", op), + }), + ReceivedBytes: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: fmt.Sprintf("%s_received_bytes", op), + Help: fmt.Sprintf("Number of bytes of %s messages received from the network", op), + }), + SentBytes: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: fmt.Sprintf("%s_sent_bytes", op), + Help: fmt.Sprintf("Size of bytes of %s messages received from the network", op), + }), + } + errs.Add( + metrics.Register(msg.NumSent), + metrics.Register(msg.NumFailed), + metrics.Register(msg.NumReceived), + metrics.Register(msg.ReceivedBytes), + metrics.Register(msg.SentBytes), + ) + + if op.Compressible() { + msg.SavedReceivedBytes = metric.NewAveragerWithErrs( + namespace, + fmt.Sprintf("%s_compression_saved_received_bytes", op), + fmt.Sprintf("bytes saved (not received) due to compression of %s messages", op), + metrics, + errs, + ) + msg.SavedSentBytes = metric.NewAveragerWithErrs( + namespace, + fmt.Sprintf("%s_compression_saved_sent_bytes", op), + fmt.Sprintf("bytes saved (not sent) due to compression of %s messages", op), + metrics, + errs, + ) + } else { + msg.SavedReceivedBytes = metric.NewNoAverager() + msg.SavedSentBytes = metric.NewNoAverager() + } + return msg +} + +type Metrics struct { + Log logging.Logger + FailedToParse prometheus.Counter + MessageMetrics map[message.Op]*MessageMetrics +} + +func NewMetrics( + log logging.Logger, + namespace string, + registerer prometheus.Registerer, +) (*Metrics, error) { + m := &Metrics{ + Log: log, + FailedToParse: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "msgs_failed_to_parse", + Help: "Number of messages that could not be parsed or were invalidly formed", + }), + MessageMetrics: make(map[message.Op]*MessageMetrics, len(message.ExternalOps)), + } + + errs := wrappers.Errs{} + errs.Add(registerer.Register(m.FailedToParse)) + for _, op := range message.ExternalOps { + m.MessageMetrics[op] = NewMessageMetrics(op, namespace, registerer, &errs) + } + return m, errs.Err +} + +func (m *Metrics) Sent(msg message.OutboundMessage) { + op := msg.Op() + msgMetrics := m.MessageMetrics[op] + if msgMetrics == nil { + m.Log.Error( + "unknown message being sent with op %s", + op, + ) + msg.DecRef() + return + } + msgMetrics.NumSent.Inc() + msgMetrics.SentBytes.Add(float64(len(msg.Bytes()))) + // assume that if [saved] == 0, [msg] wasn't compressed + if saved := msg.BytesSavedCompression(); saved != 0 { + msgMetrics.SavedSentBytes.Observe(float64(saved)) + } + msg.DecRef() +} + +func (m *Metrics) MultipleSendsFailed(op message.Op, count int) { + msgMetrics := m.MessageMetrics[op] + if msgMetrics == nil { + m.Log.Error( + "%d unknown messages failed to be sent with op %s", + count, + op, + ) + return + } + msgMetrics.NumFailed.Add(float64(count)) +} + +func (m *Metrics) SendFailed(msg message.OutboundMessage) { + op := msg.Op() + msgMetrics := m.MessageMetrics[op] + if msgMetrics == nil { + m.Log.Error( + "unknown message failed to be sent with op %s", + op, + ) + msg.DecRef() + return + } + msgMetrics.NumFailed.Inc() + msg.DecRef() +} + +func (m *Metrics) Received(msg message.InboundMessage, msgLen uint32) { + op := msg.Op() + msgMetrics := m.MessageMetrics[op] + if msgMetrics == nil { + m.Log.Error( + "unknown message received with op %s", + op, + ) + return + } + msgMetrics.NumReceived.Inc() + msgMetrics.ReceivedBytes.Add(float64(msgLen)) + // assume that if [saved] == 0, [msg] wasn't compressed + if saved := msg.BytesSavedCompression(); saved != 0 { + msgMetrics.SavedReceivedBytes.Observe(float64(saved)) + } +} diff --git a/network/peer/network.go b/network/peer/network.go new file mode 100644 index 000000000000..8572b8501bb6 --- /dev/null +++ b/network/peer/network.go @@ -0,0 +1,43 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/utils" +) + +// Network defines the interface that is used by a peer to help establish a well +// connected p2p network. +type Network interface { + // Connected is called by the peer once the handshake is finished. + Connected(ids.ShortID) + + // AllowConnection enables the network is signal to the peer that its + // connection is no longer desired and should be terminated. + AllowConnection(ids.ShortID) bool + + // Track allows the peer to notify the network of a potential new peer to + // connect to. + Track(utils.IPCertDesc) + + // Disconnected is called when the peer finishes shutting down. It is not + // guaranteed that [Connected] was called for the provided peer. However, it + // is guaranteed that [Connected] will not be called after [Disconnected] + // for a given [Peer] object. + Disconnected(ids.ShortID) + + // Version provides the peer with the Version message to send to the peer + // during the handshake. + Version() (message.OutboundMessage, error) + + // Peers provides the peer with the PeerList message to send to the peer + // during the handshake. + Peers() (message.OutboundMessage, error) + + // Pong provides the peer with a Pong message to send to the peer in + // response to a Ping message. + Pong(ids.ShortID) (message.OutboundMessage, error) +} diff --git a/network/peer/peer.go b/network/peer/peer.go new file mode 100644 index 000000000000..db5febe4fa79 --- /dev/null +++ b/network/peer/peer.go @@ -0,0 +1,815 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "bufio" + "bytes" + "context" + "crypto/x509" + "encoding/binary" + "errors" + "io" + "math" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/formatting" + "github.com/ava-labs/avalanchego/utils/json" + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/ava-labs/avalanchego/version" +) + +var ( + errClosed = errors.New("closed") + + _ Peer = &peer{} +) + +// Peer encapsulates all of the functionality required to send and receive +// messages with a remote peer. +type Peer interface { + // ID returns the nodeID of the remote peer. + ID() ids.ShortID + + // Cert returns the certificate that the remote peer is using to + // authenticate their messages. + Cert() *x509.Certificate + + // LastSent returns the last time a message was sent to the peer. + LastSent() time.Time + + // LastReceived returns the last time a message was received from the peer. + LastReceived() time.Time + + // Ready returns true if the peer has finished the p2p handshake and is + // ready to send and receive messages. + Ready() bool + + // AwaitReady will block until the peer has finished the p2p handshake. If + // the context is cancelled or the peer starts closing, then an error will + // be returned. + AwaitReady(ctx context.Context) error + + // Info returns a description of the state of this peer. It should only be + // called after [Ready] returns true. + Info() Info + + // IP returns the claimed IP and signature provided by this peer during the + // handshake. It should only be called after [Ready] returns true. + IP() *SignedIP + + // Version returns the claimed node version this peer is running. It should + // only be called after [Ready] returns true. + Version() version.Application + + // TrackedSubnets returns the subnets this peer is running. It should only + // be called after [Ready] returns true. + TrackedSubnets() ids.Set + + // ObservedUptime returns the local node's uptime according to the peer. The + // value ranges from [0, 100]. It should only be called after [Ready] + // returns true. + ObservedUptime() uint8 + + // Send attempts to send [msg] to the peer. The peer takes ownership of + // [msg] for reference counting. This returns false if the message is + // guaranteed not to be delivered to the peer. + Send(msg message.OutboundMessage) bool + + // StartClose will begin shutting down the peer. It will not block. + StartClose() + + // Closed returns true once the peer has been fully shutdown. It is + // guaranteed that no more messages will be received by this peer once this + // returns true. + Closed() bool + + // AwaitClosed will block until the peer has been fully shutdown. If the + // context is cancelled, then an error will be returned. + AwaitClosed(ctx context.Context) error +} + +type peer struct { + *Config + + // the connection object that is used to read/write messages from + conn net.Conn + + // [cert] is this peer's certificate, specifically the leaf of the + // certificate chain they provided. + cert *x509.Certificate + + // node ID of this peer. + id ids.ShortID + + // ip is the claimed IP the peer gave us in the Version message. + ip *SignedIP + // version is the claimed version the peer is running that we received in + // the Version message. + version version.Application + // trackedSubnets is the subset of subnetIDs the peer sent us in the Version + // message that we are also tracking. + trackedSubnets ids.Set + + observedUptimeLock sync.RWMutex + // [observedUptimeLock] must be held while accessing [observedUptime] + observedUptime uint8 + + // True if this peer has sent us a valid Version message and + // is running a compatible version. + // Only modified on the connection's reader routine. + gotVersion utils.AtomicBool + + // True if the peer: + // * Has sent us a Version message + // * Has sent us a PeerList message + // * Is running a compatible version + // Only modified on the connection's reader routine. + finishedHandshake utils.AtomicBool + + // onFinishHandshake is closed when the peer finishes the p2p handshake. + onFinishHandshake chan struct{} + + // numExecuting is the number of goroutines this peer is currently using + numExecuting int64 + startClosingOnce sync.Once + // onClosing is closed when the peer starts closing + onClosing chan struct{} + + // onClosed is closed when the peer is closed + onClosed chan struct{} + + // Signalled when a message is added to [sendQueue], and when [p.closing] is + // set to true. [sendQueueCond.L] must be held when using [sendQueue] and + // [canSend]. + sendQueueCond *sync.Cond + + // closing flags whether the peer has started shutting down. + closing bool + + // canSend flags whether the send queue has been closed. This is separate + // from [closing] because it's possible for the send queue to be flushed + // before [StartClose] is called. + canSend bool + + // queue of the messages to be sent to this peer + sendQueue []message.OutboundMessage + + // Unix time of the last message sent and received respectively + // Must only be accessed atomically + lastSent, lastReceived int64 +} + +func Start( + config *Config, + conn net.Conn, + cert *x509.Certificate, + id ids.ShortID, +) Peer { + p := &peer{ + Config: config, + conn: conn, + cert: cert, + id: id, + onFinishHandshake: make(chan struct{}), + numExecuting: 3, + onClosing: make(chan struct{}), + onClosed: make(chan struct{}), + sendQueueCond: sync.NewCond(&sync.Mutex{}), + canSend: true, + } + + p.trackedSubnets.Add(constants.PrimaryNetworkID) + + // Make sure that the version is the first message sent + msg, err := p.Network.Version() + p.Log.AssertNoError(err) + p.Send(msg) + + go p.readMessages() + go p.writeMessages() + go p.sendPings() + + return p +} + +func (p *peer) ID() ids.ShortID { return p.id } + +func (p *peer) Cert() *x509.Certificate { return p.cert } + +func (p *peer) LastSent() time.Time { + return time.Unix( + atomic.LoadInt64(&p.lastSent), + 0, + ) +} + +func (p *peer) LastReceived() time.Time { + return time.Unix( + atomic.LoadInt64(&p.lastReceived), + 0, + ) +} + +func (p *peer) Ready() bool { return p.finishedHandshake.GetValue() } + +func (p *peer) AwaitReady(ctx context.Context) error { + select { + case <-p.onFinishHandshake: + return nil + case <-p.onClosed: + return errClosed + case <-ctx.Done(): + return ctx.Err() + } +} + +func (p *peer) Info() Info { + publicIPStr := "" + if !p.ip.IP.IP.IsZero() { + publicIPStr = p.ip.IP.IP.String() + } + return Info{ + IP: p.conn.RemoteAddr().String(), + PublicIP: publicIPStr, + ID: p.id.PrefixedString(constants.NodeIDPrefix), + Version: p.version.String(), + LastSent: time.Unix(atomic.LoadInt64(&p.lastSent), 0), + LastReceived: time.Unix(atomic.LoadInt64(&p.lastReceived), 0), + ObservedUptime: json.Uint8(p.ObservedUptime()), + TrackedSubnets: p.trackedSubnets.List(), + } +} + +func (p *peer) IP() *SignedIP { return p.ip } + +func (p *peer) Version() version.Application { return p.version } + +func (p *peer) TrackedSubnets() ids.Set { return p.trackedSubnets } + +func (p *peer) ObservedUptime() uint8 { + p.observedUptimeLock.RLock() + uptime := p.observedUptime + p.observedUptimeLock.RUnlock() + return uptime +} + +func (p *peer) Send(msg message.OutboundMessage) bool { + // Acquire space on the outbound message queue, or drop [msg] if we can't. + if !p.OutboundMsgThrottler.Acquire(msg, p.id) { + p.Log.Debug( + "dropping %s message to %s%s due to rate-limiting", + msg.Op(), + constants.NodeIDPrefix, p.id, + ) + p.Metrics.SendFailed(msg) + return false + } + + // Invariant: must call p.outboundMsgThrottler.Release(msg, p.id) when done + // sending [msg] or when we give up sending [msg]. + + p.sendQueueCond.L.Lock() + defer p.sendQueueCond.L.Unlock() + + if !p.canSend { + p.Log.Debug( + "dropping %s message to %s%s due to a closed connection", + msg.Op(), + constants.NodeIDPrefix, p.id, + ) + p.OutboundMsgThrottler.Release(msg, p.id) + p.Metrics.SendFailed(msg) + return false + } + + p.sendQueue = append(p.sendQueue, msg) + p.sendQueueCond.Signal() + return true +} + +func (p *peer) StartClose() { + p.startClosingOnce.Do(func() { + if err := p.conn.Close(); err != nil { + p.Log.Debug( + "closing connection to %s%s resulted in an error: %s", + constants.NodeIDPrefix, p.id, + err, + ) + } + + // The lock is grabbed here to avoid any potential race conditions + // causing the [Broadcast] to be dropped. + p.sendQueueCond.L.Lock() + p.closing = true + // Per [p.sendQueueCond]'s spec, it is signalled when [p.closing] is set + // to true so that we exit the WriteMessages goroutine. + p.sendQueueCond.Broadcast() + p.sendQueueCond.L.Unlock() + + close(p.onClosing) + }) +} + +func (p *peer) Closed() bool { + select { + case _, ok := <-p.onClosed: + return !ok + default: + return false + } +} + +func (p *peer) AwaitClosed(ctx context.Context) error { + select { + case <-p.onClosed: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +// close should be called at the end of each goroutine that has been spun up. +// When the last goroutine is exiting, the peer will be marked as closed. +func (p *peer) close() { + if atomic.AddInt64(&p.numExecuting, -1) != 0 { + return + } + + p.Network.Disconnected(p.id) + close(p.onClosed) +} + +// Read and handle messages from this peer. +// When this method returns, the connection is closed. +func (p *peer) readMessages() { + // Track this node with the inbound message throttler. + p.InboundMsgThrottler.AddNode(p.id) + defer func() { + p.InboundMsgThrottler.RemoveNode(p.id) + p.StartClose() + p.close() + }() + + // Continuously read and handle messages from this peer. + reader := bufio.NewReader(p.conn) + msgLenBytes := make([]byte, wrappers.IntLen) + for { + // Time out and close connection if we can't read the message length + if err := p.conn.SetReadDeadline(p.nextTimeout()); err != nil { + p.Log.Verbo( + "error setting the connection read timeout on %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + return + } + + // Read the message length + if _, err := io.ReadFull(reader, msgLenBytes); err != nil { + p.Log.Verbo( + "error reading from %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + return + } + + // Parse the message length + msgLen := binary.BigEndian.Uint32(msgLenBytes) + + // Make sure the message length is valid. + if msgLen > constants.DefaultMaxMessageSize { + p.Log.Verbo( + "too large message length %d from %s%s", + msgLen, + constants.NodeIDPrefix, p.id, + ) + return + } + + // TODO: allow cancelation here to avoid blocking when the connection is + // shutting down. + // Wait until the throttler says we can proceed to read the message. + // Note that when we are done handling this message, or give up + // trying to read it, we must call [p.InboundMsgThrottler.Release] + // to give back the bytes used by this message. + p.InboundMsgThrottler.Acquire(uint64(msgLen), p.id) + + // Invariant: When done processing this message, onFinishedHandling() is + // called exactly once. If this is not honored, the message throttler + // will leak until no new messages can be read. You can look at message + // throttler metrics to verify that there is no leak. + onFinishedHandling := func() { p.InboundMsgThrottler.Release(uint64(msgLen), p.id) } + + // Time out and close connection if we can't read message + if err := p.conn.SetReadDeadline(p.nextTimeout()); err != nil { + p.Log.Verbo( + "error setting the connection read timeout on %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + onFinishedHandling() + return + } + + // Read the message + msgBytes := make([]byte, msgLen) + if _, err := io.ReadFull(reader, msgBytes); err != nil { + p.Log.Verbo( + "error reading from %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + onFinishedHandling() + return + } + + p.Log.Verbo( + "parsing message from %s%s:\n%s", + constants.NodeIDPrefix, p.id, + formatting.DumpBytes(msgBytes), + ) + + // Parse the message + msg, err := p.MessageCreator.Parse(msgBytes, p.id, onFinishedHandling) + if err != nil { + p.Log.Verbo( + "failed to parse message from %s%s: %s\n%s", + constants.NodeIDPrefix, p.id, + err, + formatting.DumpBytes(msgBytes), + ) + + p.Metrics.FailedToParse.Inc() + + // Couldn't parse the message. Read the next one. + onFinishedHandling() + continue + } + + now := p.Clock.Time().Unix() + atomic.StoreInt64(&p.Config.LastReceived, now) + atomic.StoreInt64(&p.lastReceived, now) + p.Metrics.Received(msg, msgLen) + + // Handle the message. Note that when we are done handling this message, + // we must call [msg.OnFinishedHandling()]. + p.handle(msg) + } +} + +func (p *peer) writeMessages() { + defer func() { + // Release the bytes of the unsent messages to the outbound message + // throttler + p.sendQueueCond.L.Lock() + p.canSend = false + for _, msg := range p.sendQueue { + p.OutboundMsgThrottler.Release(msg, p.id) + p.Metrics.SendFailed(msg) + msg.DecRef() + } + p.sendQueue = nil + p.sendQueueCond.L.Unlock() + + p.StartClose() + p.close() + }() + + var reader bytes.Reader + writer := bufio.NewWriter(p.conn) + for { // When this loop exits, p.sendQueueCond.L is unlocked + p.sendQueueCond.L.Lock() + for { + if p.closing { + p.sendQueueCond.L.Unlock() + return + } + if len(p.sendQueue) > 0 { + // There is a message to send + break + } + // Wait until there is a message to send + p.sendQueueCond.Wait() + } + msg := p.sendQueue[0] + p.sendQueue = p.sendQueue[1:] + p.sendQueueCond.L.Unlock() + + msgBytes := msg.Bytes() + p.Log.Verbo( + "sending message to %s%s:\n%s", + constants.NodeIDPrefix, p.id, + formatting.DumpBytes(msgBytes), + ) + + msgLen := uint32(len(msgBytes)) + + msgLenBytes := [wrappers.IntLen]byte{} + binary.BigEndian.PutUint32(msgLenBytes[:], msgLen) + + for _, byteSlice := range [2][]byte{msgLenBytes[:], msgBytes} { + reader.Reset(byteSlice) + if err := p.conn.SetWriteDeadline(p.nextTimeout()); err != nil { + p.Log.Verbo( + "error setting write deadline to %s%s due to: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.OutboundMsgThrottler.Release(msg, p.id) + msg.DecRef() + return + } + if _, err := io.CopyN(writer, &reader, int64(len((byteSlice)))); err != nil { + p.Log.Verbo( + "error writing to %s%s due to: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.OutboundMsgThrottler.Release(msg, p.id) + msg.DecRef() + return + } + } + + // Make sure the peer got the entire message + if err := writer.Flush(); err != nil { + p.Log.Verbo( + "couldn't flush writer to %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.OutboundMsgThrottler.Release(msg, p.id) + msg.DecRef() + return + } + + p.OutboundMsgThrottler.Release(msg, p.id) + + now := p.Clock.Time().Unix() + atomic.StoreInt64(&p.Config.LastSent, now) + atomic.StoreInt64(&p.lastSent, now) + p.Metrics.Sent(msg) + } +} + +func (p *peer) sendPings() { + sendPingsTicker := time.NewTicker(p.PingFrequency) + defer func() { + sendPingsTicker.Stop() + + p.StartClose() + p.close() + }() + + for { + select { + case <-sendPingsTicker.C: + if !p.Network.AllowConnection(p.id) { + p.Log.Debug( + "disconnecting from peer %s%s because the peer's connection is no longer desired", + constants.NodeIDPrefix, p.id, + ) + return + } + + if p.finishedHandshake.GetValue() { + if err := p.VersionCompatibility.Compatible(p.version); err != nil { + p.Log.Debug( + "disconnecting from peer %s%s version (%s) not compatible: %s", + constants.NodeIDPrefix, p.id, + p.version, + err, + ) + return + } + } + + msg, err := p.MessageCreator.Ping() + p.Log.AssertNoError(err) + p.Send(msg) + case <-p.onClosing: + return + } + } +} + +func (p *peer) handle(msg message.InboundMessage) { + op := msg.Op() + switch op { // Network-related message types + case message.Ping: + p.handlePing(msg) + msg.OnFinishedHandling() + return + case message.Pong: + p.handlePong(msg) + msg.OnFinishedHandling() + return + case message.Version: + p.handleVersion(msg) + msg.OnFinishedHandling() + return + case message.PeerList: + p.handlePeerList(msg) + msg.OnFinishedHandling() + return + } + if !p.finishedHandshake.GetValue() { + p.Log.Debug( + "dropping %s from %s%s because handshake isn't finished", + op, + constants.NodeIDPrefix, p.id, + ) + msg.OnFinishedHandling() + return + } + + // Consensus and app-level messages + p.Router.HandleInbound(msg) +} + +func (p *peer) handlePing(_ message.InboundMessage) { + msg, err := p.Network.Pong(p.id) + p.Log.AssertNoError(err) + p.Send(msg) +} + +func (p *peer) handlePong(msg message.InboundMessage) { + uptime := msg.Get(message.Uptime).(uint8) + if uptime > 100 { + return + } + + p.observedUptimeLock.Lock() + p.observedUptime = uptime // [0, 100] percentage + p.observedUptimeLock.Unlock() +} + +func (p *peer) handleVersion(msg message.InboundMessage) { + if p.gotVersion.GetValue() { + p.Log.Verbo( + "dropping duplicated version message from %s%s", + constants.NodeIDPrefix, p.id, + ) + return + } + + if peerNetworkID := msg.Get(message.NetworkID).(uint32); peerNetworkID != p.NetworkID { + p.Log.Debug( + "networkID of %s%s (%d) doesn't match our's (%d)", + constants.NodeIDPrefix, p.id, + peerNetworkID, + p.NetworkID, + ) + p.StartClose() + return + } + + myTime := float64(p.Clock.Unix()) + peerTime := float64(msg.Get(message.MyTime).(uint64)) + if math.Abs(peerTime-myTime) > p.MaxClockDifference.Seconds() { + if p.Beacons.Contains(p.id) { + p.Log.Warn( + "beacon %s%s reports time (%d) that is too far out of sync with our's (%d)", + constants.NodeIDPrefix, p.id, + uint64(peerTime), + uint64(myTime), + ) + } else { + p.Log.Debug( + "peer %s%s reports time (%d) that is too far out of sync with our's (%d)", + constants.NodeIDPrefix, p.id, + uint64(peerTime), + uint64(myTime), + ) + } + p.StartClose() + return + } + + peerVersionStr := msg.Get(message.VersionStr).(string) + peerVersion, err := p.VersionParser.Parse(peerVersionStr) + if err != nil { + p.Log.Debug( + "version of %s%s could not be parsed: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.StartClose() + return + } + p.version = peerVersion + + if p.VersionCompatibility.Version().Before(peerVersion) { + if p.Beacons.Contains(p.id) { + p.Log.Info( + "beacon %s%s attempting to connect with newer version %s. You may want to update your client", + constants.NodeIDPrefix, p.id, + peerVersion, + ) + } else { + p.Log.Debug( + "peer %s%s attempting to connect with newer version %s. You may want to update your client", + constants.NodeIDPrefix, p.id, + peerVersion, + ) + } + } + + if err := p.VersionCompatibility.Compatible(peerVersion); err != nil { + p.Log.Verbo("peer %s%s version (%s) not compatible: %s", + constants.NodeIDPrefix, p.id, + peerVersion, + err, + ) + p.StartClose() + return + } + + // Note that it is expected that the [versionTime] can be in the past. We + // are just verifying that the claimed signing time isn't too far in the + // future here. + versionTime := msg.Get(message.VersionTime).(uint64) + if float64(versionTime)-myTime > p.MaxClockDifference.Seconds() { + p.Log.Debug( + "peer %s%s attempting to connect with version timestamp (%d) too far in the future", + constants.NodeIDPrefix, p.id, + versionTime, + ) + p.StartClose() + return + } + + peerIP := msg.Get(message.IP).(utils.IPDesc) + + // handle subnet IDs + subnetIDsBytes := msg.Get(message.TrackedSubnets).([][]byte) + for _, subnetIDBytes := range subnetIDsBytes { + subnetID, err := ids.ToID(subnetIDBytes) + if err != nil { + p.Log.Debug( + "tracked subnet of %s%s could not be parsed: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.StartClose() + return + } + // add only if we also track this subnet + if p.MySubnets.Contains(subnetID) { + p.trackedSubnets.Add(subnetID) + } + } + + p.ip = &SignedIP{ + IP: UnsignedIP{ + IP: peerIP, + Timestamp: versionTime, + }, + Signature: msg.Get(message.SigBytes).([]byte), + } + if err := p.ip.verify(p.cert); err != nil { + p.Log.Debug("signature verification failed for %s%s: %s", + constants.NodeIDPrefix, p.id, + err, + ) + p.StartClose() + return + } + + p.gotVersion.SetValue(true) + + peerlistMsg, err := p.Network.Peers() + p.Log.AssertNoError(err) + p.Send(peerlistMsg) +} + +func (p *peer) handlePeerList(msg message.InboundMessage) { + if !p.finishedHandshake.GetValue() { + if !p.gotVersion.GetValue() { + return + } + + p.Network.Connected(p.id) + p.finishedHandshake.SetValue(true) + close(p.onFinishHandshake) + } + + ips := msg.Get(message.SignedPeers).([]utils.IPCertDesc) + for _, ip := range ips { + p.Network.Track(ip) + } +} + +func (p *peer) nextTimeout() time.Time { + return p.Clock.Time().Add(p.PongTimeout) +} diff --git a/network/peer/peer_test.go b/network/peer/peer_test.go new file mode 100644 index 000000000000..42a78b45fb42 --- /dev/null +++ b/network/peer/peer_test.go @@ -0,0 +1,254 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "context" + "crypto" + "crypto/x509" + "net" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/assert" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/network/throttling" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/version" +) + +type testPeer struct { + Peer + inboundMsgChan <-chan message.InboundMessage +} + +type rawTestPeer struct { + config *Config + conn net.Conn + cert *x509.Certificate + nodeID ids.ShortID + inboundMsgChan <-chan message.InboundMessage +} + +func newMessageCreator(t *testing.T) message.Creator { + t.Helper() + mc, err := message.NewCreator( + prometheus.NewRegistry(), + true, + "", + 10*time.Second, + ) + assert.NoError(t, err) + return mc +} + +func makeRawTestPeers(t *testing.T) (*rawTestPeer, *rawTestPeer) { + t.Helper() + assert := assert.New(t) + + conn0, conn1 := net.Pipe() + + tlsCert0, err := staking.NewTLSCert() + assert.NoError(err) + + tlsCert1, err := staking.NewTLSCert() + assert.NoError(err) + + nodeID0 := CertToID(tlsCert0.Leaf) + nodeID1 := CertToID(tlsCert1.Leaf) + + mc := newMessageCreator(t) + + metrics, err := NewMetrics( + logging.NoLog{}, + "", + prometheus.NewRegistry(), + ) + assert.NoError(err) + + sharedConfig := Config{ + Metrics: metrics, + MessageCreator: mc, + Log: logging.NoLog{}, + InboundMsgThrottler: throttling.NewNoInboundThrottler(), + OutboundMsgThrottler: throttling.NewNoOutboundThrottler(), + VersionCompatibility: version.GetCompatibility(constants.LocalID), + VersionParser: version.NewDefaultApplicationParser(), + MySubnets: ids.Set{}, + Beacons: validators.NewSet(), + NetworkID: constants.LocalID, + PingFrequency: constants.DefaultPingFrequency, + PongTimeout: constants.DefaultPingPongTimeout, + MaxClockDifference: time.Minute, + } + peerConfig0 := sharedConfig + peerConfig1 := sharedConfig + + peerConfig0.Network = &testNetwork{ + mc: mc, + + networkID: constants.LocalID, + ip: utils.IPDesc{ + IP: net.IPv6loopback, + Port: 0, + }, + version: version.CurrentApp, + signer: tlsCert0.PrivateKey.(crypto.Signer), + subnets: ids.Set{}, + + uptime: 100, + } + inboundMsgChan0 := make(chan message.InboundMessage) + peerConfig0.Router = router.InboundHandlerFunc(func(msg message.InboundMessage) { + inboundMsgChan0 <- msg + }) + + peerConfig1.Network = &testNetwork{ + mc: mc, + + networkID: constants.LocalID, + ip: utils.IPDesc{ + IP: net.IPv6loopback, + Port: 1, + }, + version: version.CurrentApp, + signer: tlsCert1.PrivateKey.(crypto.Signer), + subnets: ids.Set{}, + + uptime: 100, + } + inboundMsgChan1 := make(chan message.InboundMessage) + peerConfig1.Router = router.InboundHandlerFunc(func(msg message.InboundMessage) { + inboundMsgChan1 <- msg + }) + + peer0 := &rawTestPeer{ + config: &peerConfig0, + conn: conn0, + cert: tlsCert0.Leaf, + nodeID: nodeID0, + inboundMsgChan: inboundMsgChan0, + } + peer1 := &rawTestPeer{ + config: &peerConfig1, + conn: conn1, + cert: tlsCert1.Leaf, + nodeID: nodeID1, + inboundMsgChan: inboundMsgChan1, + } + return peer0, peer1 +} + +func makeTestPeers(t *testing.T) (*testPeer, *testPeer) { + rawPeer0, rawPeer1 := makeRawTestPeers(t) + + peer0 := &testPeer{ + Peer: Start( + rawPeer0.config, + rawPeer0.conn, + rawPeer1.cert, + rawPeer1.nodeID, + ), + inboundMsgChan: rawPeer0.inboundMsgChan, + } + peer1 := &testPeer{ + Peer: Start( + rawPeer1.config, + rawPeer1.conn, + rawPeer0.cert, + rawPeer0.nodeID, + ), + inboundMsgChan: rawPeer1.inboundMsgChan, + } + return peer0, peer1 +} + +func makeReadyTestPeers(t *testing.T) (*testPeer, *testPeer) { + t.Helper() + assert := assert.New(t) + + peer0, peer1 := makeTestPeers(t) + + err := peer0.AwaitReady(context.Background()) + assert.NoError(err) + isReady := peer0.Ready() + assert.True(isReady) + + err = peer1.AwaitReady(context.Background()) + assert.NoError(err) + isReady = peer1.Ready() + assert.True(isReady) + + return peer0, peer1 +} + +func TestReady(t *testing.T) { + assert := assert.New(t) + + rawPeer0, rawPeer1 := makeRawTestPeers(t) + + peer0 := Start( + rawPeer0.config, + rawPeer0.conn, + rawPeer1.cert, + rawPeer1.nodeID, + ) + + isReady := peer0.Ready() + assert.False(isReady) + + peer1 := Start( + rawPeer1.config, + rawPeer1.conn, + rawPeer0.cert, + rawPeer0.nodeID, + ) + + err := peer0.AwaitReady(context.Background()) + assert.NoError(err) + isReady = peer0.Ready() + assert.True(isReady) + + err = peer1.AwaitReady(context.Background()) + assert.NoError(err) + isReady = peer1.Ready() + assert.True(isReady) + + peer0.StartClose() + err = peer0.AwaitClosed(context.Background()) + assert.NoError(err) + err = peer1.AwaitClosed(context.Background()) + assert.NoError(err) +} + +func TestSend(t *testing.T) { + assert := assert.New(t) + + peer0, peer1 := makeReadyTestPeers(t) + mc := newMessageCreator(t) + + outboundGetMsg, err := mc.Get(ids.Empty, 1, time.Second, ids.Empty) + assert.NoError(err) + + sent := peer0.Send(outboundGetMsg) + assert.True(sent) + + inboundGetMsg := <-peer1.inboundMsgChan + assert.Equal(message.Get, inboundGetMsg.Op()) + + peer1.StartClose() + err = peer0.AwaitClosed(context.Background()) + assert.NoError(err) + err = peer1.AwaitClosed(context.Background()) + assert.NoError(err) +} diff --git a/network/peer/set.go b/network/peer/set.go new file mode 100644 index 000000000000..29f7957db950 --- /dev/null +++ b/network/peer/set.go @@ -0,0 +1,157 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/sampler" +) + +var _ Set = &set{} + +func NoPrecondition(Peer) bool { return true } + +// Set contains a group of peers. +type Set interface { + // Add this peer to the set. + // + // If a peer with the same [peer.ID] is already in the set, then the new + // peer instance will replace the old peer instance. + // + // Add does not change the [peer.ID] returned from calls to [GetByIndex]. + Add(peer Peer) + + // GetByID attempts to fetch a [peer] whose [peer.ID] is equal to [nodeID]. + // If no such peer exists in the set, then [false] will be returned. + GetByID(nodeID ids.ShortID) (Peer, bool) + + // GetByIndex attempts to fetch a peer who has been allocated [index]. If + // [index] < 0 or [index] >= [Len], then false will be returned. + GetByIndex(index int) (Peer, bool) + + // Remove any [peer] whose [peer.ID] is equal to [nodeID] from the set. + Remove(nodeID ids.ShortID) + + // Len returns the number of peers currently in this set. + Len() int + + // Sample attempts to return a random slice of peers with length [n]. The + // slice will not inclide any duplicates. Only peers that cause the + // [precondition] to return true will be returned in the slice. + Sample(n int, precondition func(Peer) bool) []Peer + + // Returns information about all the peers. + AllInfo() []Info + + // Info returns information about the requested peers if they are in the + // set. + Info(nodeIDs []ids.ShortID) []Info +} + +type set struct { + peersMap map[ids.ShortID]int // nodeID -> peer's index in peersSlice + peersSlice []Peer // invariant: len(peersSlice) == len(peersMap) +} + +// NewSet returns a set that does not internally manage synchronization. +// +// Only [Add] and [Remove] require exclusion on the data structure. The +// remaining methods are safe for concurrent use. +func NewSet() Set { + return &set{ + peersMap: make(map[ids.ShortID]int), + } +} + +func (s *set) Add(peer Peer) { + nodeID := peer.ID() + index, ok := s.peersMap[nodeID] + if !ok { + s.peersMap[nodeID] = len(s.peersSlice) + s.peersSlice = append(s.peersSlice, peer) + } else { + s.peersSlice[index] = peer + } +} + +func (s *set) GetByID(nodeID ids.ShortID) (Peer, bool) { + index, ok := s.peersMap[nodeID] + if !ok { + return nil, false + } + return s.peersSlice[index], true +} + +func (s *set) GetByIndex(index int) (Peer, bool) { + if index < 0 || index >= len(s.peersSlice) { + return nil, false + } + return s.peersSlice[index], true +} + +func (s *set) Remove(nodeID ids.ShortID) { + index, ok := s.peersMap[nodeID] + if !ok { + return + } + + lastIndex := len(s.peersSlice) - 1 + lastPeer := s.peersSlice[lastIndex] + lastPeerID := lastPeer.ID() + + s.peersMap[lastPeerID] = index + s.peersSlice[index] = lastPeer + + delete(s.peersMap, nodeID) + s.peersSlice[lastIndex] = nil + s.peersSlice = s.peersSlice[:lastIndex] +} + +func (s *set) Len() int { + return len(s.peersSlice) +} + +func (s *set) Sample(n int, precondition func(Peer) bool) []Peer { + if n <= 0 { + return nil + } + + sampler := sampler.NewUniform() + // It is impossible for the sampler to report an error here. Since + // [len(s.peersSlice)] <= MaxInt64. + _ = sampler.Initialize(uint64(len(s.peersSlice))) + + peers := make([]Peer, 0, n) + for len(peers) < n { + index, err := sampler.Next() + if err != nil { + // We have run out of peers to attempt to sample. + break + } + peer := s.peersSlice[index] + if !precondition(peer) { + continue + } + peers = append(peers, peer) + } + return peers +} + +func (s *set) AllInfo() []Info { + peerInfo := make([]Info, len(s.peersSlice)) + for i, peer := range s.peersSlice { + peerInfo[i] = peer.Info() + } + return peerInfo +} + +func (s *set) Info(nodeIDs []ids.ShortID) []Info { + peerInfo := make([]Info, 0, len(nodeIDs)) + for _, nodeID := range nodeIDs { + if peer, ok := s.GetByID(nodeID); ok { + peerInfo = append(peerInfo, peer.Info()) + } + } + return peerInfo +} diff --git a/network/peer/set_test.go b/network/peer/set_test.go new file mode 100644 index 000000000000..cc2bff5d68a7 --- /dev/null +++ b/network/peer/set_test.go @@ -0,0 +1,134 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ava-labs/avalanchego/ids" +) + +func TestSet(t *testing.T) { + assert := assert.New(t) + + set := NewSet() + + peer1 := &peer{ + id: ids.ShortID{0x01}, + observedUptime: 0, + } + updatedPeer1 := &peer{ + id: ids.ShortID{0x01}, + observedUptime: 1, + } + peer2 := &peer{ + id: ids.ShortID{0x02}, + } + unknownPeer := &peer{ + id: ids.ShortID{0xff}, + } + peer3 := &peer{ + id: ids.ShortID{0x03}, + } + peer4 := &peer{ + id: ids.ShortID{0x04}, + } + + // add of first peer is handled + set.Add(peer1) + retrievedPeer1, peer1Found := set.GetByID(peer1.id) + assert.True(peer1Found) + assert.Equal(peer1.ObservedUptime(), retrievedPeer1.ObservedUptime()) + assert.Equal(1, set.Len()) + + // re-addition of peer works as update + set.Add(updatedPeer1) + retrievedPeer1, peer1Found = set.GetByID(peer1.id) + assert.True(peer1Found) + assert.Equal(updatedPeer1.ObservedUptime(), retrievedPeer1.ObservedUptime()) + assert.Equal(1, set.Len()) + + // add of another peer is handled + set.Add(peer2) + retrievedPeer2, peer2Found := set.GetByID(peer2.id) + assert.True(peer2Found) + assert.Equal(peer2.ObservedUptime(), retrievedPeer2.ObservedUptime()) + assert.Equal(2, set.Len()) + + // removal of added peer is handled + set.Remove(peer1.id) + _, peer1Found = set.GetByID(peer1.id) + assert.False(peer1Found) + retrievedPeer2, peer2Found = set.GetByID(peer2.id) + assert.True(peer2Found) + assert.Equal(peer2.id, retrievedPeer2.ID()) + assert.Equal(1, set.Len()) + + // query for unknown peer is handled + _, unknownPeerfound := set.GetByID(unknownPeer.id) + assert.False(unknownPeerfound) + + // removal of unknown peer is handled + set.Remove(unknownPeer.id) + retrievedPeer2, peer2Found = set.GetByID(peer2.id) + assert.True(peer2Found) + assert.Equal(peer2.id, retrievedPeer2.ID()) + assert.Equal(1, set.Len()) + + // retrival by inbound index is handled + set.Add(peer3) + set.Add(peer4) + assert.Equal(3, set.Len()) + + thirdPeer, ok := set.GetByIndex(1) + assert.True(ok) + assert.Equal(peer3.id, thirdPeer.ID()) + + // retrival by out-of-bounds index is handled + _, ok = set.GetByIndex(3) + assert.False(ok) +} + +func TestSetSample(t *testing.T) { + assert := assert.New(t) + + set := NewSet() + + peer1 := &peer{ + id: ids.ShortID{0x01}, + } + peer2 := &peer{ + id: ids.ShortID{0x02}, + } + + // Case: Empty + peers := set.Sample(0, NoPrecondition) + assert.Empty(peers) + + peers = set.Sample(-1, NoPrecondition) + assert.Empty(peers) + + peers = set.Sample(1, NoPrecondition) + assert.Empty(peers) + + // Case: 1 peer + set.Add(peer1) + + peers = set.Sample(0, NoPrecondition) + assert.Empty(peers) + + peers = set.Sample(1, NoPrecondition) + assert.Equal(peers, []Peer{peer1}) + + peers = set.Sample(2, NoPrecondition) + assert.Equal(peers, []Peer{peer1}) + + // Case: 2 peers + set.Add(peer2) + + peers = set.Sample(1, NoPrecondition) + assert.Len(peers, 1) +} diff --git a/network/peer/test_network.go b/network/peer/test_network.go new file mode 100644 index 000000000000..3e41933bc46b --- /dev/null +++ b/network/peer/test_network.go @@ -0,0 +1,65 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "crypto" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/version" +) + +var _ Network = &testNetwork{} + +type testNetwork struct { + mc message.Creator + + networkID uint32 + ip utils.IPDesc + version version.Application + signer crypto.Signer + subnets ids.Set + + uptime uint8 +} + +func (n *testNetwork) Connected(ids.ShortID) {} + +func (n *testNetwork) AllowConnection(ids.ShortID) bool { return true } + +func (n *testNetwork) Track(utils.IPCertDesc) {} + +func (n *testNetwork) Disconnected(ids.ShortID) {} + +func (n *testNetwork) Version() (message.OutboundMessage, error) { + now := uint64(time.Now().Unix()) + unsignedIP := UnsignedIP{ + IP: n.ip, + Timestamp: now, + } + signedIP, err := unsignedIP.Sign(n.signer) + if err != nil { + return nil, err + } + return n.mc.Version( + n.networkID, + now, + n.ip, + n.version.String(), + now, + signedIP.Signature, + n.subnets.List(), + ) +} + +func (n *testNetwork) Peers() (message.OutboundMessage, error) { + return n.mc.PeerList(nil, true) +} + +func (n *testNetwork) Pong(ids.ShortID) (message.OutboundMessage, error) { + return n.mc.Pong(n.uptime) +} diff --git a/network/peer/test_peer.go b/network/peer/test_peer.go new file mode 100644 index 000000000000..455e27bb5bd9 --- /dev/null +++ b/network/peer/test_peer.go @@ -0,0 +1,120 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peer + +import ( + "context" + "crypto" + "net" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/network/throttling" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/version" +) + +// StartTestPeer provides a simple interface to create a peer that has finished +// the p2p handshake. +// +// This function will generate a new TLS key to use when connecting to the peer. +// +// The returned peer will not throttle inbound or outbound messages. +// +// - [ctx] provides a way of canceling the connection request. +// - [ip] is the remote that will be dialed to create the connection. +// - [networkID] will be sent to the peer during the handshake. If the peer is +// expecting a different [networkID], the handshake will fail and an error +// will be returned. +// - [router] will be called with all non-handshake messages received by the +// peer. +func StartTestPeer( + ctx context.Context, + ip utils.IPDesc, + networkID uint32, + router router.InboundHandler, +) (Peer, error) { + dialer := net.Dialer{} + conn, err := dialer.DialContext(ctx, constants.NetworkType, ip.String()) + if err != nil { + return nil, err + } + + tlsCert, err := staking.NewTLSCert() + if err != nil { + return nil, err + } + + tlsConfg := TLSConfig(*tlsCert) + clientUpgrader := NewTLSClientUpgrader(tlsConfg) + + peerID, conn, cert, err := clientUpgrader.Upgrade(conn) + if err != nil { + return nil, err + } + + mc, err := message.NewCreator( + prometheus.NewRegistry(), + true, + "", + 10*time.Second, + ) + if err != nil { + return nil, err + } + + metrics, err := NewMetrics( + logging.NoLog{}, + "", + prometheus.NewRegistry(), + ) + if err != nil { + return nil, err + } + + peer := Start( + &Config{ + Metrics: metrics, + MessageCreator: mc, + Log: logging.NoLog{}, + InboundMsgThrottler: throttling.NewNoInboundThrottler(), + OutboundMsgThrottler: throttling.NewNoOutboundThrottler(), + Network: &testNetwork{ + mc: mc, + + networkID: networkID, + ip: utils.IPDesc{ + IP: net.IPv6zero, + Port: 0, + }, + version: version.CurrentApp, + signer: tlsCert.PrivateKey.(crypto.Signer), + subnets: ids.Set{}, + + uptime: 100, + }, + Router: router, + VersionCompatibility: version.GetCompatibility(networkID), + VersionParser: version.NewDefaultApplicationParser(), + MySubnets: ids.Set{}, + Beacons: validators.NewSet(), + NetworkID: networkID, + PingFrequency: constants.DefaultPingFrequency, + PongTimeout: constants.DefaultPingPongTimeout, + MaxClockDifference: time.Minute, + }, + conn, + cert, + peerID, + ) + return peer, peer.AwaitReady(ctx) +} diff --git a/network/tls_config.go b/network/peer/tls_config.go similarity index 97% rename from network/tls_config.go rename to network/peer/tls_config.go index c8483d1303cc..192a8a280eda 100644 --- a/network/tls_config.go +++ b/network/peer/tls_config.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package network +package peer import "crypto/tls" diff --git a/network/upgrader.go b/network/peer/upgrader.go similarity index 78% rename from network/upgrader.go rename to network/peer/upgrader.go index adae67abc3b3..f0817333c931 100644 --- a/network/upgrader.go +++ b/network/peer/upgrader.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package network +package peer import ( "crypto/tls" @@ -14,9 +14,10 @@ import ( ) var ( - errNoCert = errors.New("tls handshake finished with no peer certificate") - _ Upgrader = &tlsServerUpgrader{} - _ Upgrader = &tlsClientUpgrader{} + errNoCert = errors.New("tls handshake finished with no peer certificate") + + _ Upgrader = &tlsServerUpgrader{} + _ Upgrader = &tlsClientUpgrader{} ) type Upgrader interface { @@ -62,11 +63,11 @@ func connToIDAndCert(conn *tls.Conn) (ids.ShortID, net.Conn, *x509.Certificate, return ids.ShortID{}, nil, nil, errNoCert } peerCert := state.PeerCertificates[0] - return certToID(peerCert), conn, peerCert, nil + return CertToID(peerCert), conn, peerCert, nil } -func certToID(cert *x509.Certificate) ids.ShortID { - return ids.ShortID( - hashing.ComputeHash160Array( - hashing.ComputeHash256(cert.Raw))) +func CertToID(cert *x509.Certificate) ids.ShortID { + return hashing.ComputeHash160Array( + hashing.ComputeHash256(cert.Raw), + ) } diff --git a/network/peer_test.go b/network/peer_test.go deleted file mode 100644 index 77fdaa01c906..000000000000 --- a/network/peer_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package network - -import ( - "context" - "crypto" - "net" - "testing" - "time" - - "github.com/prometheus/client_golang/prometheus" - - "github.com/stretchr/testify/assert" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/message" - "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/utils" - "github.com/ava-labs/avalanchego/utils/hashing" -) - -func TestPeer_Close(t *testing.T) { - initCerts(t) - - ip := utils.NewDynamicIPDesc( - net.IPv6loopback, - 0, - ) - id := ids.ShortID(hashing.ComputeHash160Array([]byte(ip.IP().String()))) - - listener := &testListener{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - inbound: make(chan net.Conn, 1<<10), - closed: make(chan struct{}), - } - caller := &testDialer{ - addr: &net.TCPAddr{ - IP: net.IPv6loopback, - Port: 0, - }, - outbounds: make(map[string]*testListener), - } - - vdrs := getDefaultManager() - beacons := validators.NewSet() - metrics := prometheus.NewRegistry() - msgCreator, err := message.NewCreator(metrics, true, "dummyNamespace", 10*time.Second) - assert.NoError(t, err) - handler := &testHandler{} - - netwrk, err := newTestNetwork( - id, - ip, - defaultVersionManager, - vdrs, - beacons, - cert0.PrivateKey.(crypto.Signer), - ids.Set{}, - tlsConfig0, - listener, - caller, - metrics, - msgCreator, - handler, - ) - assert.NoError(t, err) - assert.NotNil(t, netwrk) - - ip1 := utils.NewDynamicIPDesc( - net.IPv6loopback, - 1, - ) - caller.outbounds[ip1.IP().String()] = listener - conn, err := caller.Dial(context.Background(), ip1.IP()) - assert.NoError(t, err) - - basenetwork := netwrk.(*network) - - newmsgbytes := []byte("hello") - - // fake a peer, and write a message - peer := newPeer(basenetwork, conn, ip1.IP()) - peer.sendQueue = make([]message.OutboundMessage, 0) - testMsg := message.NewTestMsg(message.GetVersion, newmsgbytes, false) - peer.Send(testMsg) - - go func() { - err := netwrk.Close() - assert.NoError(t, err) - }() - - peer.Close() -} diff --git a/network/peers_data.go b/network/peers_data.go deleted file mode 100644 index c11c0604b8cb..000000000000 --- a/network/peers_data.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package network - -import ( - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/sampler" -) - -// peersData encapsulate all peers known to Network. -// peers in peersData can be retrieved by their id and can be iterated over -// via getList() method, returning a slice of peers. -// Index associated with peers MAY CHANGE following removal of other peers - -type peersData struct { - peersIdxes map[ids.ShortID]int // peerID -> *peer index in peersList - peersList []*peer // invariant: len(peersList) == len(peersIdxes) -} - -func (p *peersData) initialize() { - p.peersIdxes = make(map[ids.ShortID]int) - p.peersList = make([]*peer, 0) -} - -func (p *peersData) reset() { - p.initialize() -} - -func (p *peersData) add(peer *peer) { - if _, ok := p.getByID(peer.nodeID); !ok { // new insertion - p.peersList = append(p.peersList, peer) - p.peersIdxes[peer.nodeID] = len(p.peersList) - 1 - } else { // update - p.peersList[p.peersIdxes[peer.nodeID]] = peer - } -} - -func (p *peersData) remove(peer *peer) { - if _, ok := p.getByID(peer.nodeID); !ok { - return - } - - // Drop p by replacing it with last peer in peersList. - // if p is already the last peer, simply drop it. - // Keep peersIdxes synced. peersList order is not preserved - idToDrop := peer.nodeID - idxToReplace := p.peersIdxes[idToDrop] - - if idxToReplace != len(p.peersList)-1 { - lastPeer := p.peersList[len(p.peersList)-1] - p.peersList[idxToReplace] = lastPeer - p.peersIdxes[lastPeer.nodeID] = idxToReplace - } - p.peersList = p.peersList[:len(p.peersList)-1] - delete(p.peersIdxes, idToDrop) -} - -func (p *peersData) getByID(id ids.ShortID) (*peer, bool) { - if idx, ok := p.peersIdxes[id]; ok { - return p.peersList[idx], ok - } - return nil, false -} - -func (p *peersData) getByIdx(idx int) (*peer, bool) { - // peer index may change following other peers removal - // since upon removal order is not guaranteed. - if idx < 0 || idx >= len(p.peersList) { - return nil, false - } - return p.peersList[idx], true -} - -func (p *peersData) size() int { - return len(p.peersList) -} - -// Randomly sample [n] peers that have finished the handshake and tracks the subnetID. -// If < [n] peers have finished the handshake and tracks the subnetID, returns < [n] peers. -// If [n] > [p.size()], returns <= [p.size()] peers. -// [n] must be >= 0. -// [p] must not be modified while this method is executing. -func (p *peersData) sample(subnetID ids.ID, validatorOnly bool, n int) ([]*peer, error) { - numPeers := p.size() - if numPeers < n { - n = numPeers - } - if n == 0 { - return nil, nil - } - s := sampler.NewUniform() - if err := s.Initialize(uint64(numPeers)); err != nil { - return nil, err - } - peers := make([]*peer, 0, n) // peers we'll gossip to - for len(peers) < n { - idx, err := s.Next() - if err != nil { - // all peers have been sampled and not enough valid ones found. - // return what we have - return peers, nil - } - peer := p.peersList[idx] - if !peer.finishedHandshake.GetValue() || !peer.trackedSubnets.Contains(subnetID) || (validatorOnly && !peer.net.config.Validators.Contains(subnetID, peer.nodeID)) { - continue - } - peers = append(peers, peer) - } - return peers, nil -} diff --git a/network/peers_data_test.go b/network/peers_data_test.go deleted file mode 100644 index dec5495ca202..000000000000 --- a/network/peers_data_test.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package network - -import ( - "testing" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/stretchr/testify/assert" -) - -func TestPeersData(t *testing.T) { - data := peersData{} - data.initialize() - - peer1 := peer{ - nodeID: ids.ShortID{0x01}, - } - - // add of first peer is handled - data.add(&peer1) - retrievedPeer1, peer1Found := data.getByID(peer1.nodeID) - assert.True(t, peer1Found) - assert.True(t, &peer1 == retrievedPeer1) - assert.True(t, data.size() == 1) - - // re-addition of peer works as update - updatedPeer1 := peer{ - nodeID: ids.ShortID{0x01}, - } - data.add(&updatedPeer1) - retrievedPeer1, peer1Found = data.getByID(peer1.nodeID) - assert.True(t, peer1Found) - assert.True(t, &updatedPeer1 == retrievedPeer1) - assert.True(t, data.size() == 1) - - peer2 := peer{ - nodeID: ids.ShortID{0x02}, - } - - // add of another peer is handled - data.add(&peer2) - retrievedPeer2, peer2Found := data.getByID(peer2.nodeID) - assert.True(t, peer2Found) - assert.True(t, &peer2 == retrievedPeer2) - assert.True(t, data.size() == 2) - - // removal of added peer is handled - data.remove(&peer1) - retrievedPeer1, peer1Found = data.getByID(peer1.nodeID) - assert.False(t, peer1Found) - assert.True(t, retrievedPeer1 == nil) - retrievedPeer2, peer2Found = data.getByID(peer2.nodeID) - assert.True(t, peer2Found) - assert.True(t, &peer2 == retrievedPeer2) - assert.True(t, data.size() == 1) - - unknownPeer := peer{ - nodeID: ids.ShortID{0xff}, - } - - // query for unknown peer is handled - retrievedUnknownPeer, unknownPeerfound := data.getByID(unknownPeer.nodeID) - assert.False(t, unknownPeerfound) - assert.True(t, retrievedUnknownPeer == nil) - - // removal of unknown peer is handled - data.remove(&unknownPeer) - retrievedPeer2, peer2Found = data.getByID(peer2.nodeID) - assert.True(t, peer2Found) - assert.True(t, &peer2 == retrievedPeer2) - assert.True(t, data.size() == 1) - - // retrival by inbound index is handled - peer3 := peer{ - nodeID: ids.ShortID{0x03}, - } - peer4 := peer{ - nodeID: ids.ShortID{0x04}, - } - data.add(&peer3) - data.add(&peer4) - assert.True(t, data.size() == 3) - - thirdPeer, ok := data.getByIdx(1) - assert.True(t, ok) - assert.True(t, &peer3 == thirdPeer) - - // retrival by outbound index is handled - outOfIndexPeer, ok := data.getByIdx(data.size()) - assert.False(t, ok) - assert.True(t, outOfIndexPeer == nil) - - // reset is idempotent - data.reset() - assert.True(t, data.size() == 0) - - data.reset() - assert.True(t, data.size() == 0) -} - -func TestPeersDataSample(t *testing.T) { - data := peersData{} - data.initialize() - trackedSubnetIDs := ids.Set{} - trackedSubnetIDs.Add(constants.PrimaryNetworkID) - networkWithValidators := &network{config: &Config{Validators: validators.NewManager()}} - // Case: Empty - peers, err := data.sample(constants.PrimaryNetworkID, false, 0) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 1) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - // Case: 1 peer who hasn't finished handshake - peer1 := peer{ - nodeID: ids.ShortID{0x01}, - trackedSubnets: trackedSubnetIDs, - net: networkWithValidators, - } - data.add(&peer1) - peers, err = data.sample(constants.PrimaryNetworkID, false, 0) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 1) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - // Case-1: peer who hasn't finished handshake, 1 who has - peer2 := peer{ - nodeID: ids.ShortID{0x02}, - trackedSubnets: trackedSubnetIDs, - } - peer2.finishedHandshake.SetValue(true) - data.add(&peer2) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 0) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 1) - assert.NoError(t, err) - assert.Len(t, peers, 1) - assert.EqualValues(t, peers[0].nodeID, peer2.nodeID) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 2) - assert.NoError(t, err) - assert.Len(t, peers, 1) - assert.EqualValues(t, peers[0].nodeID, peer2.nodeID) - - // Case-2: peers who have finished handshake - peer1.finishedHandshake.SetValue(true) - peers, err = data.sample(constants.PrimaryNetworkID, false, 0) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 1) - assert.NoError(t, err) - assert.Len(t, peers, 1) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 2) - assert.NoError(t, err) - assert.Len(t, peers, 2) - // Ensure both peers are sampled once - assert.True(t, - (peers[0].nodeID == peer1.nodeID && peers[1].nodeID == peer2.nodeID) || - (peers[0].nodeID == peer2.nodeID && peers[1].nodeID == peer1.nodeID), - ) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 3) - assert.NoError(t, err) - assert.Len(t, peers, 2) - // Ensure both peers are sampled once - assert.True(t, - (peers[0].nodeID == peer1.nodeID && peers[1].nodeID == peer2.nodeID) || - (peers[0].nodeID == peer2.nodeID && peers[1].nodeID == peer1.nodeID), - ) - - // Case-3: peers who track testSubnet - testSubnetID := ids.GenerateTestID() - - // no peers has this subnet - peers, err = data.sample(testSubnetID, false, 3) - assert.NoError(t, err) - assert.Len(t, peers, 0) - - // peer with additional subnet sampled - newSubnetSet := ids.Set{} - newSubnetSet.Add(constants.PrimaryNetworkID, testSubnetID) - - peer3 := peer{ - nodeID: ids.ShortID{0x03}, - trackedSubnets: newSubnetSet, - } - peer3.finishedHandshake.SetValue(true) - data.add(&peer3) - - peers, err = data.sample(testSubnetID, false, 3) - assert.NoError(t, err) - assert.Len(t, peers, 1) - - // Ensure peer is sampled - assert.Equal(t, peer3.nodeID, peers[0].nodeID) - - peers, err = data.sample(constants.PrimaryNetworkID, false, 3) - assert.NoError(t, err) - assert.Len(t, peers, 3) -} diff --git a/network/throttling/bandwidth_throttler.go b/network/throttling/bandwidth_throttler.go index a84b463de023..be176ef6fcdd 100644 --- a/network/throttling/bandwidth_throttler.go +++ b/network/throttling/bandwidth_throttler.go @@ -117,7 +117,7 @@ func (t *bandwidthThrottler) Acquire(msgSize uint64, nodeID ids.ShortID) { return } // TODO Allow cancellation using context? - if err := limiter.WaitN(context.Background(), int(msgSize)); err != nil { + if err := limiter.WaitN(context.TODO(), int(msgSize)); err != nil { // This should never happen. t.log.Warn("error while awaiting %d bytes for %s: %s", msgSize, nodeID.PrefixedString(constants.NodeIDPrefix), err) } diff --git a/network/tracked_ip.go b/network/tracked_ip.go new file mode 100644 index 000000000000..643ceefdd41b --- /dev/null +++ b/network/tracked_ip.go @@ -0,0 +1,73 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "math/rand" + "sync" + "time" + + "github.com/ava-labs/avalanchego/network/peer" +) + +func init() { rand.Seed(time.Now().UnixNano()) } + +type trackedIP struct { + delayLock sync.RWMutex + delay time.Duration + + ip *peer.UnsignedIP + + stopTrackingOnce sync.Once + onStopTracking chan struct{} +} + +func newTrackedIP(ip *peer.UnsignedIP) *trackedIP { + return &trackedIP{ + ip: ip, + onStopTracking: make(chan struct{}), + } +} + +func (ip *trackedIP) trackNewIP(newIP *peer.UnsignedIP) *trackedIP { + ip.stopTracking() + return &trackedIP{ + delay: ip.getDelay(), + ip: newIP, + onStopTracking: make(chan struct{}), + } +} + +func (ip *trackedIP) getDelay() time.Duration { + ip.delayLock.RLock() + delay := ip.delay + ip.delayLock.RUnlock() + return delay +} + +func (ip *trackedIP) increaseDelay(initialDelay, maxDelay time.Duration) { + ip.delayLock.Lock() + defer ip.delayLock.Unlock() + + // If the timeout was previously 0, ensure that there is a reasonable delay. + if ip.delay <= 0 { + ip.delay = initialDelay + } + + // Randomization is only performed here to distribute reconnection + // attempts to a node that previously shut down. This doesn't + // require cryptographically secure random number generation. + // set the timeout to [1, 2) * timeout + ip.delay = time.Duration(float64(ip.delay) * (1 + rand.Float64())) // #nosec G404 + if ip.delay > maxDelay { + // set the timeout to [.75, 1) * maxDelay + ip.delay = time.Duration(float64(maxDelay) * (3 + rand.Float64()) / 4) // #nosec G404 + } +} + +func (ip *trackedIP) stopTracking() { + ip.stopTrackingOnce.Do(func() { + close(ip.onStopTracking) + }) +} diff --git a/network/tracked_ip_test.go b/network/tracked_ip_test.go new file mode 100644 index 000000000000..e2b3cc479a19 --- /dev/null +++ b/network/tracked_ip_test.go @@ -0,0 +1,48 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package network + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestTrackedIP(t *testing.T) { + assert := assert.New(t) + + ip := trackedIP{ + onStopTracking: make(chan struct{}), + } + + assert.Equal(time.Duration(0), ip.getDelay()) + + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), 2*time.Second) + + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), 4*time.Second) + + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), 8*time.Second) + + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), 16*time.Second) + + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), 32*time.Second) + + for i := 0; i < 100; i++ { + ip.increaseDelay(time.Second, time.Minute) + assert.LessOrEqual(ip.getDelay(), time.Minute) + } + assert.GreaterOrEqual(ip.getDelay(), 45*time.Second) + + ip.stopTracking() + <-ip.onStopTracking + + ip.stopTracking() + <-ip.onStopTracking +} diff --git a/node/node.go b/node/node.go index cc70efc67a1f..0b4f6529e989 100644 --- a/node/node.go +++ b/node/node.go @@ -38,6 +38,8 @@ import ( "github.com/ava-labs/avalanchego/ipcs" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network" + "github.com/ava-labs/avalanchego/network/dialer" + "github.com/ava-labs/avalanchego/network/peer" "github.com/ava-labs/avalanchego/network/throttling" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/networking/benchlist" @@ -173,7 +175,7 @@ func (n *Node) initNetworking() error { return err } // Wrap listener so it will only accept a certain number of incoming connections per second - listener = throttling.NewThrottledListener(listener, n.Config.NetworkConfig.ThrottlerConfig.MaxIncomingConnsPerSec) + listener = throttling.NewThrottledListener(listener, n.Config.NetworkConfig.ThrottlerConfig.MaxInboundConnsPerSec) ipDesc, err := utils.ToIPDesc(listener.Addr().String()) if err != nil { @@ -191,7 +193,7 @@ func (n *Node) initNetworking() error { return errInvalidTLSKey } - tlsConfig := network.TLSConfig(n.Config.StakingTLSCert) + tlsConfig := peer.TLSConfig(n.Config.StakingTLSCert) // Initialize validator manager and primary network's validator set primaryNetworkValidators := validators.NewSet() @@ -266,6 +268,7 @@ func (n *Node) initNetworking() error { n.MetricsRegisterer, n.Log, listener, + dialer.NewDialer(constants.NetworkType, n.Config.NetworkConfig.DialerConfig, n.Log), consensusRouter, n.benchlistManager, ) @@ -357,10 +360,8 @@ func (n *Node) Dispatch() error { }) // Add bootstrap nodes to the peer network - for _, peerIP := range n.Config.BootstrapIPs { - if !peerIP.Equal(n.Config.IP.IP()) { - n.Net.TrackIP(peerIP) - } + for i, peerIP := range n.Config.BootstrapIPs { + n.Net.ManuallyTrack(n.Config.BootstrapIDs[i], peerIP) } // Start P2P connections @@ -864,9 +865,11 @@ func (n *Node) initInfoAPI() error { n.Log, n.chainManager, n.Config.VMManager, + &n.Config.NetworkConfig.MyIP, n.Net, version.NewDefaultApplicationParser(), primaryValidators, + n.benchlistManager, ) if err != nil { return err @@ -1065,10 +1068,7 @@ func (n *Node) Initialize( n.Log = logger n.Config = config var err error - n.ID, err = ids.ToShortID(hashing.PubkeyBytesToAddress(n.Config.StakingTLSCert.Leaf.Raw)) - if err != nil { - return fmt.Errorf("problem deriving node ID from certificate: %w", err) - } + n.ID = peer.CertToID(n.Config.StakingTLSCert.Leaf) n.LogFactory = logFactory n.DoneShuttingDown.Add(1) n.Log.Info("node version is: %s", version.CurrentApp) @@ -1209,8 +1209,7 @@ func (n *Node) shutdown() { n.profiler.Shutdown() } if n.Net != nil { - // Close already logs its own error if one occurs, so the error is ignored here - _ = n.Net.Close() + n.Net.StartClose() } if err := n.APIServer.Shutdown(); err != nil { n.Log.Debug("error during API shutdown: %s", err) diff --git a/scripts/tests.e2e.sh b/scripts/tests.e2e.sh index f7849dec7ece..2c6ba5ce6c2f 100755 --- a/scripts/tests.e2e.sh +++ b/scripts/tests.e2e.sh @@ -22,7 +22,7 @@ fi # TODO: migrate to upstream avalanche-network-runner GOARCH=$(go env GOARCH) GOOS=$(go env GOOS) -NETWORK_RUNNER_VERSION=1.0.5 +NETWORK_RUNNER_VERSION=1.0.6 DOWNLOAD_PATH=/tmp/avalanche-network-runner.tar.gz DOWNLOAD_URL=https://github.com/ava-labs/avalanche-network-runner/releases/download/v${NETWORK_RUNNER_VERSION}/avalanche-network-runner_${NETWORK_RUNNER_VERSION}_linux_amd64.tar.gz if [[ ${GOOS} == "darwin" ]]; then diff --git a/scripts/tests.upgrade.sh b/scripts/tests.upgrade.sh index 6466852a3216..2be7c5bb0de3 100755 --- a/scripts/tests.upgrade.sh +++ b/scripts/tests.upgrade.sh @@ -55,7 +55,7 @@ find /tmp/avalanchego-v${VERSION} # download avalanche-network-runner # https://github.com/ava-labs/avalanche-network-runner # TODO: migrate to upstream avalanche-network-runner -NETWORK_RUNNER_VERSION=1.0.5 +NETWORK_RUNNER_VERSION=1.0.6 DOWNLOAD_PATH=/tmp/avalanche-network-runner.tar.gz DOWNLOAD_URL=https://github.com/ava-labs/avalanche-network-runner/releases/download/v${NETWORK_RUNNER_VERSION}/avalanche-network-runner_${NETWORK_RUNNER_VERSION}_linux_amd64.tar.gz if [[ ${GOOS} == "darwin" ]]; then diff --git a/scripts/versions.sh b/scripts/versions.sh index 0455ff046044..89f11068870e 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -7,4 +7,4 @@ # Set up the versions to be used # Don't export them as their used in the context of other calls -coreth_version=${CORETH_VERSION:-'v0.8.5-rc.2'} +coreth_version=${CORETH_VERSION:-'v0.8.7-rc.2'} diff --git a/snow/engine/avalanche/state/unique_vertex_test.go b/snow/engine/avalanche/state/unique_vertex_test.go index fa74c24d59bb..c7c5b264db8e 100644 --- a/snow/engine/avalanche/state/unique_vertex_test.go +++ b/snow/engine/avalanche/state/unique_vertex_test.go @@ -284,6 +284,30 @@ func TestUniqueVertexCacheMiss(t *testing.T) { validateVertex(vtx, choices.Processing) } +func TestParseVertexWithIncorrectChainID(t *testing.T) { + statelessVertex, err := vertex.Build( // regular, non-stop vertex + ids.GenerateTestID(), + 0, + nil, + [][]byte{{1}}, + ) + if err != nil { + t.Fatal(err) + } + vtxBytes := statelessVertex.Bytes() + + s := newTestSerializer(t, func(b []byte) (snowstorm.Tx, error) { + if bytes.Equal(b, []byte{1}) { + return &snowstorm.TestTx{}, nil + } + return nil, errors.New("invalid tx") + }) + + if _, err := s.ParseVtx(vtxBytes); err == nil { + t.Fatal("should have failed to parse the vertex due to invalid chainID") + } +} + func TestParseVertexWithInvalidTxs(t *testing.T) { ctx := snow.DefaultContextTest() statelessVertex, err := vertex.Build( // regular, non-stop vertex diff --git a/snow/networking/benchlist/manager.go b/snow/networking/benchlist/manager.go index 578329d90846..e00494a46aa3 100644 --- a/snow/networking/benchlist/manager.go +++ b/snow/networking/benchlist/manager.go @@ -52,7 +52,6 @@ type Config struct { MinimumFailingDuration time.Duration `json:"minimumFailingDuration"` Duration time.Duration `json:"duration"` MaxPortion float64 `json:"maxPortion"` - PeerSummaryEnabled bool `json:"peerSummaryEnabled"` } type manager struct { diff --git a/snow/networking/router/inbound_handler.go b/snow/networking/router/inbound_handler.go new file mode 100644 index 000000000000..80081ab1ea65 --- /dev/null +++ b/snow/networking/router/inbound_handler.go @@ -0,0 +1,34 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package router + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/message" + "github.com/ava-labs/avalanchego/version" +) + +var _ InboundHandler = InboundHandlerFunc(nil) + +// InboundHandler handles inbound messages +type InboundHandler interface { + HandleInbound(msg message.InboundMessage) +} + +// The ExternalRouterFunc type is an adapter to allow the use of ordinary +// functions as ExternalRouters. If f is a function with the appropriate +// signature, ExternalRouterFunc(f) is an ExternalRouter that calls f. +type InboundHandlerFunc func(msg message.InboundMessage) + +func (f InboundHandlerFunc) HandleInbound(msg message.InboundMessage) { + f(msg) +} + +// ExternalHandler handles messages from external parties +type ExternalHandler interface { + InboundHandler + + Connected(nodeID ids.ShortID, nodeVersion version.Application) + Disconnected(nodeID ids.ShortID) +} diff --git a/snow/networking/router/router.go b/snow/networking/router/router.go index 543fe4447d07..9488a1c6ec86 100644 --- a/snow/networking/router/router.go +++ b/snow/networking/router/router.go @@ -15,14 +15,13 @@ import ( "github.com/ava-labs/avalanchego/snow/networking/handler" "github.com/ava-labs/avalanchego/snow/networking/timeout" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/version" ) // Router routes consensus messages to the Handler of the consensus // engine that the messages are intended for type Router interface { - ExternalRouter - InternalRouter + ExternalHandler + InternalHandler Initialize( nodeID ids.ShortID, @@ -41,10 +40,9 @@ type Router interface { health.Checker } -// ExternalRouter routes messages from the network to the -// Handler of the consensus engine that the message is intended for -type ExternalRouter interface { - HandleInbound(msg message.InboundMessage) +// InternalHandler deals with messages internal to this node +type InternalHandler interface { + benchlist.Benchable RegisterRequest( nodeID ids.ShortID, @@ -53,11 +51,3 @@ type ExternalRouter interface { op message.Op, ) } - -// InternalRouter deals with messages internal to this node -type InternalRouter interface { - benchlist.Benchable - - Connected(nodeID ids.ShortID, nodeVersion version.Application) - Disconnected(nodeID ids.ShortID) -} diff --git a/utils/beacon/beacon.go b/utils/beacon/beacon.go new file mode 100644 index 000000000000..a559d8256588 --- /dev/null +++ b/utils/beacon/beacon.go @@ -0,0 +1,31 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package beacon + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils" +) + +var _ Beacon = &beacon{} + +type Beacon interface { + ID() ids.ShortID + IP() utils.IPDesc +} + +type beacon struct { + id ids.ShortID + ip utils.IPDesc +} + +func New(id ids.ShortID, ip utils.IPDesc) Beacon { + return &beacon{ + id: id, + ip: ip, + } +} + +func (b *beacon) ID() ids.ShortID { return b.id } +func (b *beacon) IP() utils.IPDesc { return b.ip } diff --git a/utils/beacon/set.go b/utils/beacon/set.go new file mode 100644 index 000000000000..5ee502886cde --- /dev/null +++ b/utils/beacon/set.go @@ -0,0 +1,131 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package beacon + +import ( + "errors" + "strings" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/constants" +) + +var ( + _ Set = &set{} + + errDuplicateID = errors.New("duplicated ID") + errDuplicateIP = errors.New("duplicated IP") + + errUnknownID = errors.New("unknown ID") + errUnknownIP = errors.New("unknown IP") +) + +type Set interface { + Add(Beacon) error + + RemoveByID(ids.ShortID) error + RemoveByIP(utils.IPDesc) error + + Len() int + + IDsArg() string + IPsArg() string +} + +type set struct { + ids map[ids.ShortID]int + ips map[string]int + beacons []Beacon +} + +func NewSet() Set { + return &set{ + ids: make(map[ids.ShortID]int), + ips: make(map[string]int), + } +} + +func (s *set) Add(b Beacon) error { + id := b.ID() + _, duplicateID := s.ids[id] + if duplicateID { + return errDuplicateID + } + + ipStr := b.IP().String() + _, duplicateIP := s.ips[ipStr] + if duplicateIP { + return errDuplicateIP + } + + s.ids[id] = len(s.beacons) + s.ips[ipStr] = len(s.beacons) + s.beacons = append(s.beacons, b) + return nil +} + +func (s *set) RemoveByID(idToRemove ids.ShortID) error { + indexToRemove, exists := s.ids[idToRemove] + if !exists { + return errUnknownID + } + toRemove := s.beacons[indexToRemove] + ipToRemove := toRemove.IP().String() + + indexToMove := len(s.beacons) - 1 + toMove := s.beacons[indexToMove] + idToMove := toMove.ID() + ipToMove := toMove.IP().String() + + s.ids[idToMove] = indexToRemove + s.ips[ipToMove] = indexToRemove + s.beacons[indexToRemove] = toMove + + delete(s.ids, idToRemove) + delete(s.ips, ipToRemove) + s.beacons[indexToMove] = nil + s.beacons = s.beacons[:indexToMove] + return nil +} + +func (s *set) RemoveByIP(ip utils.IPDesc) error { + indexToRemove, exists := s.ips[ip.String()] + if !exists { + return errUnknownIP + } + toRemove := s.beacons[indexToRemove] + idToRemove := toRemove.ID() + return s.RemoveByID(idToRemove) +} + +func (s *set) Len() int { return len(s.beacons) } + +func (s *set) IDsArg() string { + sb := strings.Builder{} + if len(s.beacons) == 0 { + return "" + } + b := s.beacons[0] + _, _ = sb.WriteString(b.ID().PrefixedString(constants.NodeIDPrefix)) + for _, b := range s.beacons[1:] { + _, _ = sb.WriteString(",") + _, _ = sb.WriteString(b.ID().PrefixedString(constants.NodeIDPrefix)) + } + return sb.String() +} + +func (s *set) IPsArg() string { + sb := strings.Builder{} + if len(s.beacons) == 0 { + return "" + } + b := s.beacons[0] + _, _ = sb.WriteString(b.IP().String()) + for _, b := range s.beacons[1:] { + _, _ = sb.WriteString(",") + _, _ = sb.WriteString(b.IP().String()) + } + return sb.String() +} diff --git a/utils/beacon/set_test.go b/utils/beacon/set_test.go new file mode 100644 index 000000000000..11687e8b94e4 --- /dev/null +++ b/utils/beacon/set_test.go @@ -0,0 +1,108 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package beacon + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils" +) + +func TestSet(t *testing.T) { + assert := assert.New(t) + + id0 := ids.ShortID{0} + id1 := ids.ShortID{1} + id2 := ids.ShortID{2} + + ip0 := utils.IPDesc{ + IP: net.IPv4zero, + Port: 0, + } + ip1 := utils.IPDesc{ + IP: net.IPv4zero, + Port: 1, + } + ip2 := utils.IPDesc{ + IP: net.IPv4zero, + Port: 2, + } + + b0 := New(id0, ip0) + b1 := New(id1, ip1) + b2 := New(id2, ip2) + + s := NewSet() + + idsArg := s.IDsArg() + assert.Equal("", idsArg) + ipsArg := s.IPsArg() + assert.Equal("", ipsArg) + len := s.Len() + assert.Equal(0, len) + + err := s.Add(b0) + assert.NoError(err) + + idsArg = s.IDsArg() + assert.Equal("NodeID-111111111111111111116DBWJs", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:0", ipsArg) + len = s.Len() + assert.Equal(1, len) + + err = s.Add(b0) + assert.ErrorIs(err, errDuplicateID) + + idsArg = s.IDsArg() + assert.Equal("NodeID-111111111111111111116DBWJs", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:0", ipsArg) + len = s.Len() + assert.Equal(1, len) + + err = s.Add(b1) + assert.NoError(err) + + idsArg = s.IDsArg() + assert.Equal("NodeID-111111111111111111116DBWJs,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:0,0.0.0.0:1", ipsArg) + len = s.Len() + assert.Equal(2, len) + + err = s.Add(b2) + assert.NoError(err) + + idsArg = s.IDsArg() + assert.Equal("NodeID-111111111111111111116DBWJs,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt,NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:0,0.0.0.0:1,0.0.0.0:2", ipsArg) + len = s.Len() + assert.Equal(3, len) + + err = s.RemoveByID(b0.ID()) + assert.NoError(err) + + idsArg = s.IDsArg() + assert.Equal("NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:2,0.0.0.0:1", ipsArg) + len = s.Len() + assert.Equal(2, len) + + err = s.RemoveByIP(b1.IP()) + assert.NoError(err) + + idsArg = s.IDsArg() + assert.Equal("NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp", idsArg) + ipsArg = s.IPsArg() + assert.Equal("0.0.0.0:2", ipsArg) + len = s.Len() + assert.Equal(1, len) +} diff --git a/utils/dynamicip/dynamicip.go b/utils/dynamicip/dynamicip.go index 8457d07f3fb8..dd56031daff6 100644 --- a/utils/dynamicip/dynamicip.go +++ b/utils/dynamicip/dynamicip.go @@ -63,7 +63,7 @@ func (r *OpenDNSResolver) IsResolver() bool { } func (r *OpenDNSResolver) Resolve() (net.IP, error) { - ip, err := r.Resolver.LookupHost(context.Background(), "myip.opendns.com") + ip, err := r.Resolver.LookupHost(context.TODO(), "myip.opendns.com") if err != nil { return nil, err } diff --git a/utils/logging/config.go b/utils/logging/config.go index c39ee2dfeead..ddab909f19c6 100644 --- a/utils/logging/config.go +++ b/utils/logging/config.go @@ -5,16 +5,32 @@ package logging import ( "fmt" + "os" "time" - "github.com/mitchellh/go-homedir" - "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/units" ) -// DefaultLogDirectory is the default directory where logs are saved -var DefaultLogDirectory = fmt.Sprintf("~/.%s/logs", constants.AppName) +var ( + homeDir = os.ExpandEnv("$HOME") + // DefaultLogDirectory is the default directory where logs are saved + DefaultLogDirectory = fmt.Sprintf("%s/.%s/logs", homeDir, constants.AppName) + + // DefaultConfig provides a reasonable default logger configuration. It + // should not be modified, it should be copied if changes are intended on + // being made. + DefaultConfig = Config{ + RotationInterval: 24 * time.Hour, + FileSize: 8 * units.MiB, + RotationSize: 7, + FlushSize: 1, + DisplayLevel: Info, + DisplayHighlight: Plain, + LogLevel: Debug, + Directory: DefaultLogDirectory, + } +) // Config defines the configuration of a logger type Config struct { @@ -30,22 +46,7 @@ type Config struct { LogLevel Level `json:"logLevel"` DisplayLevel Level `json:"displayLevel"` DisplayHighlight Highlight `json:"displayHighlight"` - Directory string `json:"-"` + Directory string `json:"directory"` MsgPrefix string `json:"-"` LoggerName string `json:"-"` } - -// DefaultConfig returns a logger configuration with default parameters -func DefaultConfig() (Config, error) { - dir, err := homedir.Expand(DefaultLogDirectory) - return Config{ - RotationInterval: 24 * time.Hour, - FileSize: 8 * units.MiB, - RotationSize: 7, - FlushSize: 1, - DisplayLevel: Info, - DisplayHighlight: Plain, - LogLevel: Debug, - Directory: dir, - }, err -} diff --git a/utils/logging/log_test.go b/utils/logging/log_test.go index cfb63aa145b7..6d0b099d1378 100644 --- a/utils/logging/log_test.go +++ b/utils/logging/log_test.go @@ -6,12 +6,7 @@ package logging import "testing" func TestLog(t *testing.T) { - config, err := DefaultConfig() - if err != nil { - t.Fatalf("Error: %s", err) - } - - log, err := NewTestLog(config) + log, err := NewTestLog(DefaultConfig) if err != nil { t.Fatalf("Error creating log: %s", err) } diff --git a/version/constants.go b/version/constants.go index 100c935892af..153f10f989fb 100644 --- a/version/constants.go +++ b/version/constants.go @@ -11,7 +11,7 @@ import ( // These are globals that describe network upgrades and node versions var ( - Current = NewDefaultVersion(1, 7, 6) + Current = NewDefaultVersion(1, 7, 7) CurrentApp = NewDefaultApplication(constants.PlatformName, Current.Major(), Current.Minor(), Current.Patch()) MinimumCompatibleVersion = NewDefaultApplication(constants.PlatformName, 1, 7, 0) PrevMinimumCompatibleVersion = NewDefaultApplication(constants.PlatformName, 1, 6, 0) diff --git a/vms/avm/service.go b/vms/avm/service.go index e8fd20d92dbd..32a2b285b5b9 100644 --- a/vms/avm/service.go +++ b/vms/avm/service.go @@ -451,16 +451,10 @@ func (service *Service) GetAllBalances(r *http.Request, args *GetAllBalancesArgs reply.Balances = make([]Balance, assetIDs.Len()) i := 0 for assetID := range assetIDs { - if alias, err := service.vm.PrimaryAlias(assetID); err == nil { - reply.Balances[i] = Balance{ - AssetID: alias, - Balance: json.Uint64(balances[assetID]), - } - } else { - reply.Balances[i] = Balance{ - AssetID: assetID.String(), - Balance: json.Uint64(balances[assetID]), - } + alias := service.vm.PrimaryAliasOrDefault(assetID) + reply.Balances[i] = Balance{ + AssetID: alias, + Balance: json.Uint64(balances[assetID]), } i++ } diff --git a/vms/components/chain/state.go b/vms/components/chain/state.go index 085731be7838..529d36ce27d2 100644 --- a/vms/components/chain/state.go +++ b/vms/components/chain/state.go @@ -177,11 +177,39 @@ func NewMeteredState( return c, nil } +// SetLastAcceptedBlock sets the last accepted block to [lastAcceptedBlock]. This should be called +// with an internal block - not a wrapped block returned from state. +// +// This also flushes [lastAcceptedBlock] from missingBlocks and unverifiedBlocks to +// ensure that their contents stay valid. +func (s *State) SetLastAcceptedBlock(lastAcceptedBlock snowman.Block) error { + if len(s.verifiedBlocks) != 0 { + return fmt.Errorf("cannot set chain state last accepted block with non-zero number of verified blocks in processing: %d", len(s.verifiedBlocks)) + } + + // [lastAcceptedBlock] is no longer missing or unverified, so we evict it from the corresponding + // caches. + // + // Note: there's no need to evict from the decided blocks cache or bytesToIDCache since their + // contents will still be valid. + lastAcceptedBlockID := lastAcceptedBlock.ID() + s.missingBlocks.Evict(lastAcceptedBlockID) + s.unverifiedBlocks.Evict(lastAcceptedBlockID) + s.lastAcceptedBlock = &BlockWrapper{ + Block: lastAcceptedBlock, + state: s, + } + s.decidedBlocks.Put(lastAcceptedBlockID, s.lastAcceptedBlock) + + return nil +} + // Flush each block cache func (s *State) Flush() { s.decidedBlocks.Flush() s.missingBlocks.Flush() s.unverifiedBlocks.Flush() + s.bytesToIDCache.Flush() } // GetBlock returns the BlockWrapper as snowman.Block corresponding to [blkID] diff --git a/vms/components/chain/state_test.go b/vms/components/chain/state_test.go index 3b8144d686e8..51cb5fcfd4ba 100644 --- a/vms/components/chain/state_test.go +++ b/vms/components/chain/state_test.go @@ -714,3 +714,136 @@ func TestStateBytesToIDCache(t *testing.T) { _, ok = chainState.bytesToIDCache.Get(string(blk1.Bytes())) assert.False(t, ok) } + +// TestSetLastAcceptedBlock ensures chainState's last accepted block +// can be updated by calling [SetLastAcceptedBlock]. +func TestSetLastAcceptedBlock(t *testing.T) { + testBlks := NewTestBlocks(1) + genesisBlock := testBlks[0] + genesisBlock.SetStatus(choices.Accepted) + + postSetBlk1ParentID := hashing.ComputeHash256Array([]byte{byte(199)}) + postSetBlk1Bytes := []byte{byte(200)} + postSetBlk2Bytes := []byte{byte(201)} + postSetBlk1 := &TestBlock{ + TestBlock: &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: hashing.ComputeHash256Array(postSetBlk1Bytes), + StatusV: choices.Accepted, + }, + HeightV: uint64(200), + BytesV: postSetBlk1Bytes, + ParentV: postSetBlk1ParentID, + }, + } + postSetBlk2 := &TestBlock{ + TestBlock: &snowman.TestBlock{ + TestDecidable: choices.TestDecidable{ + IDV: hashing.ComputeHash256Array(postSetBlk2Bytes), + StatusV: choices.Processing, + }, + HeightV: uint64(201), + BytesV: postSetBlk2Bytes, + ParentV: postSetBlk1.IDV, + }, + } + // note we do not need to parse postSetBlk1 so it is omitted here + testBlks = append(testBlks, postSetBlk2) + + getBlock, parseBlock, getCanonicalBlockID := createInternalBlockFuncs(t, testBlks) + chainState := NewState(&Config{ + LastAcceptedBlock: genesisBlock, + GetBlock: getBlock, + UnmarshalBlock: parseBlock, + BuildBlock: cantBuildBlock, + GetBlockIDAtHeight: getCanonicalBlockID, + }) + lastAcceptedID, err := chainState.LastAccepted() + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != genesisBlock.ID() { + t.Fatal("Expected last accepted block to be the genesis block") + } + + // call SetLastAcceptedBlock for postSetBlk1 + if err := chainState.SetLastAcceptedBlock(postSetBlk1); err != nil { + t.Fatal(err) + } + lastAcceptedID, err = chainState.LastAccepted() + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != postSetBlk1.ID() { + t.Fatal("Expected last accepted block to be postSetBlk1") + } + if lastAcceptedID = chainState.LastAcceptedBlock().ID(); lastAcceptedID != postSetBlk1.ID() { + t.Fatal("Expected last accepted block to be postSetBlk1") + } + + // ensure further blocks can be accepted + parsedpostSetBlk2, err := chainState.ParseBlock(postSetBlk2.Bytes()) + if err != nil { + t.Fatal("Failed to parse postSetBlk2 due to: %w", err) + } + if err := parsedpostSetBlk2.Verify(); err != nil { + t.Fatal("Parsed postSetBlk2 failed verification unexpectedly due to %w", err) + } + if err := parsedpostSetBlk2.Accept(); err != nil { + t.Fatal(err) + } + lastAcceptedID, err = chainState.LastAccepted() + if err != nil { + t.Fatal(err) + } + if lastAcceptedID != postSetBlk2.ID() { + t.Fatal("Expected last accepted block to be postSetBlk2") + } + if lastAcceptedID = chainState.LastAcceptedBlock().ID(); lastAcceptedID != postSetBlk2.ID() { + t.Fatal("Expected last accepted block to be postSetBlk2") + } + + checkAcceptedBlock(t, chainState, parsedpostSetBlk2, false) +} + +func TestSetLastAcceptedBlockWithProcessingBlocksErrors(t *testing.T) { + testBlks := NewTestBlocks(5) + genesisBlock := testBlks[0] + genesisBlock.SetStatus(choices.Accepted) + blk1 := testBlks[1] + resetBlk := testBlks[4] + + getBlock, parseBlock, getCanonicalBlockID := createInternalBlockFuncs(t, testBlks) + buildBlock := func() (snowman.Block, error) { + // Once the block is built, mark it as processing + blk1.SetStatus(choices.Processing) + return blk1, nil + } + + chainState := NewState(&Config{ + DecidedCacheSize: 2, + MissingCacheSize: 2, + UnverifiedCacheSize: 2, + BytesToIDCacheSize: 2, + LastAcceptedBlock: genesisBlock, + GetBlock: getBlock, + UnmarshalBlock: parseBlock, + BuildBlock: buildBlock, + GetBlockIDAtHeight: getCanonicalBlockID, + }) + + builtBlk, err := chainState.BuildBlock() + if err != nil { + t.Fatal(err) + } + assert.Len(t, chainState.verifiedBlocks, 0) + + if err := builtBlk.Verify(); err != nil { + t.Fatalf("Built block failed verification due to %s", err) + } + assert.Len(t, chainState.verifiedBlocks, 1) + + checkProcessingBlock(t, chainState, builtBlk) + + assert.Error(t, chainState.SetLastAcceptedBlock(resetBlk), "should have errored resetting chain state with processing block") +} diff --git a/vms/mock_manager.go b/vms/mock_manager.go index 385f68348cd0..2328aac55378 100644 --- a/vms/mock_manager.go +++ b/vms/mock_manager.go @@ -162,6 +162,20 @@ func (mr *MockManagerMockRecorder) PrimaryAlias(id interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryAlias", reflect.TypeOf((*MockManager)(nil).PrimaryAlias), id) } +// PrimaryAliasOrDefault mocks base method. +func (m *MockManager) PrimaryAliasOrDefault(id ids.ID) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PrimaryAliasOrDefault", id) + ret0, _ := ret[0].(string) + return ret0 +} + +// PrimaryAliasOrDefault indicates an expected call of PrimaryAliasOrDefault. +func (mr *MockManagerMockRecorder) PrimaryAliasOrDefault(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrimaryAliasOrDefault", reflect.TypeOf((*MockManager)(nil).PrimaryAliasOrDefault), id) +} + // RegisterFactory mocks base method. func (m *MockManager) RegisterFactory(vmID ids.ID, factory Factory) error { m.ctrl.T.Helper() diff --git a/vms/rpcchainvm/factory.go b/vms/rpcchainvm/factory.go index f0b40052a85b..2f9fe9de13fb 100644 --- a/vms/rpcchainvm/factory.go +++ b/vms/rpcchainvm/factory.go @@ -5,8 +5,10 @@ package rpcchainvm import ( "errors" + "fmt" "io/ioutil" "log" + "path/filepath" "google.golang.org/grpc" @@ -85,22 +87,27 @@ func (f *factory) New(ctx *snow.Context) (interface{}, error) { } client := plugin.NewClient(config) + pluginName := filepath.Base(f.path) + pluginErr := func(err error) error { + return fmt.Errorf("plugin: %q: %w", pluginName, err) + } + rpcClient, err := client.Client() if err != nil { client.Kill() - return nil, err + return nil, pluginErr(err) } raw, err := rpcClient.Dispense("vm") if err != nil { client.Kill() - return nil, err + return nil, pluginErr(err) } vm, ok := raw.(*VMClient) if !ok { client.Kill() - return nil, errWrongVM + return nil, pluginErr(errWrongVM) } vm.SetProcess(client) diff --git a/vms/rpcchainvm/ghttp/gconn/conn_client.go b/vms/rpcchainvm/ghttp/gconn/conn_client.go index e05de4302a91..ebd440ff4e01 100644 --- a/vms/rpcchainvm/ghttp/gconn/conn_client.go +++ b/vms/rpcchainvm/ghttp/gconn/conn_client.go @@ -10,6 +10,8 @@ import ( "net" "time" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/ava-labs/avalanchego/api/proto/gconnproto" "github.com/ava-labs/avalanchego/utils/wrappers" ) @@ -65,7 +67,7 @@ func (c *Client) Write(b []byte) (int, error) { } func (c *Client) Close() error { - _, err := c.client.Close(context.Background(), &gconnproto.CloseRequest{}) + _, err := c.client.Close(context.Background(), &emptypb.Empty{}) errs := wrappers.Errs{} errs.Add(err) for _, toClose := range c.toClose { @@ -93,7 +95,7 @@ func (c *Client) SetReadDeadline(t time.Time) error { if err != nil { return err } - _, err = c.client.SetReadDeadline(context.Background(), &gconnproto.SetReadDeadlineRequest{ + _, err = c.client.SetReadDeadline(context.Background(), &gconnproto.SetDeadlineRequest{ Time: bytes, }) return err @@ -104,7 +106,7 @@ func (c *Client) SetWriteDeadline(t time.Time) error { if err != nil { return err } - _, err = c.client.SetWriteDeadline(context.Background(), &gconnproto.SetWriteDeadlineRequest{ + _, err = c.client.SetWriteDeadline(context.Background(), &gconnproto.SetDeadlineRequest{ Time: bytes, }) return err diff --git a/vms/rpcchainvm/ghttp/gconn/conn_server.go b/vms/rpcchainvm/ghttp/gconn/conn_server.go index c60849ad66a1..bb623ea6e57d 100644 --- a/vms/rpcchainvm/ghttp/gconn/conn_server.go +++ b/vms/rpcchainvm/ghttp/gconn/conn_server.go @@ -8,6 +8,8 @@ import ( "net" "time" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/ava-labs/avalanchego/api/proto/gconnproto" "github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils" ) @@ -52,35 +54,35 @@ func (s *Server) Write(ctx context.Context, req *gconnproto.WriteRequest) (*gcon }, nil } -func (s *Server) Close(ctx context.Context, req *gconnproto.CloseRequest) (*gconnproto.CloseResponse, error) { +func (s *Server) Close(ctx context.Context, req *emptypb.Empty) (*emptypb.Empty, error) { err := s.conn.Close() s.closer.Stop() - return &gconnproto.CloseResponse{}, err + return &emptypb.Empty{}, err } -func (s *Server) SetDeadline(ctx context.Context, req *gconnproto.SetDeadlineRequest) (*gconnproto.SetDeadlineResponse, error) { +func (s *Server) SetDeadline(ctx context.Context, req *gconnproto.SetDeadlineRequest) (*emptypb.Empty, error) { deadline := time.Time{} err := deadline.UnmarshalBinary(req.Time) if err != nil { return nil, err } - return &gconnproto.SetDeadlineResponse{}, s.conn.SetDeadline(deadline) + return &emptypb.Empty{}, s.conn.SetDeadline(deadline) } -func (s *Server) SetReadDeadline(ctx context.Context, req *gconnproto.SetReadDeadlineRequest) (*gconnproto.SetReadDeadlineResponse, error) { +func (s *Server) SetReadDeadline(ctx context.Context, req *gconnproto.SetDeadlineRequest) (*emptypb.Empty, error) { deadline := time.Time{} err := deadline.UnmarshalBinary(req.Time) if err != nil { return nil, err } - return &gconnproto.SetReadDeadlineResponse{}, s.conn.SetReadDeadline(deadline) + return &emptypb.Empty{}, s.conn.SetReadDeadline(deadline) } -func (s *Server) SetWriteDeadline(ctx context.Context, req *gconnproto.SetWriteDeadlineRequest) (*gconnproto.SetWriteDeadlineResponse, error) { +func (s *Server) SetWriteDeadline(ctx context.Context, req *gconnproto.SetDeadlineRequest) (*emptypb.Empty, error) { deadline := time.Time{} err := deadline.UnmarshalBinary(req.Time) if err != nil { return nil, err } - return &gconnproto.SetWriteDeadlineResponse{}, s.conn.SetWriteDeadline(deadline) + return &emptypb.Empty{}, s.conn.SetWriteDeadline(deadline) } diff --git a/vms/rpcchainvm/ghttp/greadcloser/reader_client.go b/vms/rpcchainvm/ghttp/greadcloser/reader_client.go deleted file mode 100644 index 06bd9e0a08ea..000000000000 --- a/vms/rpcchainvm/ghttp/greadcloser/reader_client.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package greadcloser - -import ( - "context" - "errors" - "io" - - "github.com/ava-labs/avalanchego/api/proto/greadcloserproto" -) - -var _ io.ReadCloser = &Client{} - -// Client is a read closer that talks over RPC. -type Client struct{ client greadcloserproto.ReaderClient } - -// NewClient returns a read closer connected to a remote read closer -func NewClient(client greadcloserproto.ReaderClient) *Client { - return &Client{client: client} -} - -func (c *Client) Read(p []byte) (int, error) { - resp, err := c.client.Read(context.Background(), &greadcloserproto.ReadRequest{ - Length: int32(len(p)), - }) - if err != nil { - return 0, err - } - - copy(p, resp.Read) - - if resp.Errored { - err = errors.New(resp.Error) - } - return len(resp.Read), err -} - -func (c *Client) Close() error { - _, err := c.client.Close(context.Background(), &greadcloserproto.CloseRequest{}) - return err -} diff --git a/vms/rpcchainvm/ghttp/greadcloser/reader_server.go b/vms/rpcchainvm/ghttp/greadcloser/reader_server.go deleted file mode 100644 index e527f06a866d..000000000000 --- a/vms/rpcchainvm/ghttp/greadcloser/reader_server.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package greadcloser - -import ( - "context" - "io" - - "github.com/ava-labs/avalanchego/api/proto/greadcloserproto" -) - -var _ greadcloserproto.ReaderServer = &Server{} - -// Server is a io.ReadCloser that is managed over RPC. -type Server struct { - greadcloserproto.UnimplementedReaderServer - readCloser io.ReadCloser -} - -// NewServer returns an io.ReadCloser instance managed remotely -func NewServer(readCloser io.ReadCloser) *Server { - return &Server{readCloser: readCloser} -} - -func (s *Server) Read(ctx context.Context, req *greadcloserproto.ReadRequest) (*greadcloserproto.ReadResponse, error) { - buf := make([]byte, int(req.Length)) - n, err := s.readCloser.Read(buf) - resp := &greadcloserproto.ReadResponse{ - Read: buf[:n], - } - if err != nil { - resp.Errored = true - resp.Error = err.Error() - } - return resp, nil -} - -func (s *Server) Close(ctx context.Context, req *greadcloserproto.CloseRequest) (*greadcloserproto.CloseResponse, error) { - return &greadcloserproto.CloseResponse{}, s.readCloser.Close() -} diff --git a/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go b/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go index ec39e2f17ed6..0bff0f1237f0 100644 --- a/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go +++ b/vms/rpcchainvm/ghttp/gresponsewriter/writer_client.go @@ -9,6 +9,8 @@ import ( "net" "net/http" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/hashicorp/go-plugin" "github.com/ava-labs/avalanchego/api/proto/gconnproto" @@ -79,7 +81,7 @@ func (c *Client) WriteHeader(statusCode int) { func (c *Client) Flush() { // TODO: is there a way to handle the error here? - _, _ = c.client.Flush(context.Background(), &gresponsewriterproto.FlushRequest{}) + _, _ = c.client.Flush(context.Background(), &emptypb.Empty{}) } type addr struct { @@ -91,33 +93,18 @@ func (a *addr) Network() string { return a.network } func (a *addr) String() string { return a.str } func (c *Client) Hijack() (net.Conn, *bufio.ReadWriter, error) { - resp, err := c.client.Hijack(context.Background(), &gresponsewriterproto.HijackRequest{}) - if err != nil { - return nil, nil, err - } - - connConn, err := c.broker.Dial(resp.ConnServer) - if err != nil { - return nil, nil, err - } - - readerConn, err := c.broker.Dial(resp.ReaderServer) + resp, err := c.client.Hijack(context.Background(), &emptypb.Empty{}) if err != nil { - // Ignore error closing resources to return original error - _ = connConn.Close() return nil, nil, err } - writerConn, err := c.broker.Dial(resp.WriterServer) + clientConn, err := c.broker.Dial(resp.ConnReadWriterServer) if err != nil { - // Ignore errors closing resources to return original error - _ = connConn.Close() - _ = readerConn.Close() return nil, nil, err } conn := gconn.NewClient( - gconnproto.NewConnClient(connConn), + gconnproto.NewConnClient(clientConn), &addr{ network: resp.LocalNetwork, str: resp.LocalString, @@ -126,13 +113,11 @@ func (c *Client) Hijack() (net.Conn, *bufio.ReadWriter, error) { network: resp.RemoteNetwork, str: resp.RemoteString, }, - connConn, - readerConn, - writerConn, + clientConn, ) - reader := greader.NewClient(greaderproto.NewReaderClient(readerConn)) - writer := gwriter.NewClient(gwriterproto.NewWriterClient(writerConn)) + reader := greader.NewClient(greaderproto.NewReaderClient(clientConn)) + writer := gwriter.NewClient(gwriterproto.NewWriterClient(clientConn)) readWriter := bufio.NewReadWriter( bufio.NewReader(reader), diff --git a/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go b/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go index 935cd04d43a3..6d1367b036d0 100644 --- a/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go +++ b/vms/rpcchainvm/ghttp/gresponsewriter/writer_server.go @@ -9,6 +9,7 @@ import ( "net/http" "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" "github.com/hashicorp/go-plugin" @@ -63,7 +64,7 @@ func (s *Server) Write(ctx context.Context, req *gresponsewriterproto.WriteReque }, nil } -func (s *Server) WriteHeader(ctx context.Context, req *gresponsewriterproto.WriteHeaderRequest) (*gresponsewriterproto.WriteHeaderResponse, error) { +func (s *Server) WriteHeader(ctx context.Context, req *gresponsewriterproto.WriteHeaderRequest) (*emptypb.Empty, error) { headers := s.writer.Header() for key := range headers { delete(headers, key) @@ -72,19 +73,19 @@ func (s *Server) WriteHeader(ctx context.Context, req *gresponsewriterproto.Writ headers[header.Key] = header.Values } s.writer.WriteHeader(int(req.StatusCode)) - return &gresponsewriterproto.WriteHeaderResponse{}, nil + return &emptypb.Empty{}, nil } -func (s *Server) Flush(ctx context.Context, req *gresponsewriterproto.FlushRequest) (*gresponsewriterproto.FlushResponse, error) { +func (s *Server) Flush(ctx context.Context, req *emptypb.Empty) (*emptypb.Empty, error) { flusher, ok := s.writer.(http.Flusher) if !ok { return nil, errUnsupportedFlushing } flusher.Flush() - return &gresponsewriterproto.FlushResponse{}, nil + return &emptypb.Empty{}, nil } -func (s *Server) Hijack(ctx context.Context, req *gresponsewriterproto.HijackRequest) (*gresponsewriterproto.HijackResponse, error) { +func (s *Server) Hijack(ctx context.Context, req *emptypb.Empty) (*gresponsewriterproto.HijackResponse, error) { hijacker, ok := s.writer.(http.Hijacker) if !ok { return nil, errUnsupportedHijacking @@ -94,12 +95,10 @@ func (s *Server) Hijack(ctx context.Context, req *gresponsewriterproto.HijackReq return nil, err } - connID := s.broker.NextId() - readerID := s.broker.NextId() - writerID := s.broker.NextId() + connReadWriterID := s.broker.NextId() closer := grpcutils.ServerCloser{} - go s.broker.AcceptAndServe(connID, func(opts []grpc.ServerOption) *grpc.Server { + go s.broker.AcceptAndServe(connReadWriterID, func(opts []grpc.ServerOption) *grpc.Server { opts = append(opts, grpc.MaxRecvMsgSize(math.MaxInt), grpc.MaxSendMsgSize(math.MaxInt), @@ -107,25 +106,7 @@ func (s *Server) Hijack(ctx context.Context, req *gresponsewriterproto.HijackReq server := grpc.NewServer(opts...) closer.Add(server) gconnproto.RegisterConnServer(server, gconn.NewServer(conn, &closer)) - return server - }) - go s.broker.AcceptAndServe(readerID, func(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, - grpc.MaxRecvMsgSize(math.MaxInt), - grpc.MaxSendMsgSize(math.MaxInt), - ) - server := grpc.NewServer(opts...) - closer.Add(server) greaderproto.RegisterReaderServer(server, greader.NewServer(readWriter)) - return server - }) - go s.broker.AcceptAndServe(writerID, func(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, - grpc.MaxRecvMsgSize(math.MaxInt), - grpc.MaxSendMsgSize(math.MaxInt), - ) - server := grpc.NewServer(opts...) - closer.Add(server) gwriterproto.RegisterWriterServer(server, gwriter.NewServer(readWriter)) return server }) @@ -134,12 +115,10 @@ func (s *Server) Hijack(ctx context.Context, req *gresponsewriterproto.HijackReq remote := conn.RemoteAddr() return &gresponsewriterproto.HijackResponse{ - ConnServer: connID, - LocalNetwork: local.Network(), - LocalString: local.String(), - RemoteNetwork: remote.Network(), - RemoteString: remote.String(), - ReaderServer: readerID, - WriterServer: writerID, + LocalNetwork: local.Network(), + LocalString: local.String(), + RemoteNetwork: remote.Network(), + RemoteString: remote.String(), + ConnReadWriterServer: connReadWriterID, }, nil } diff --git a/vms/rpcchainvm/ghttp/http_client.go b/vms/rpcchainvm/ghttp/http_client.go index 638743fe3e48..2af6ab0c5174 100644 --- a/vms/rpcchainvm/ghttp/http_client.go +++ b/vms/rpcchainvm/ghttp/http_client.go @@ -4,6 +4,7 @@ package ghttp import ( + "io" "net/http" "google.golang.org/grpc" @@ -11,10 +12,8 @@ import ( "github.com/hashicorp/go-plugin" "github.com/ava-labs/avalanchego/api/proto/ghttpproto" - "github.com/ava-labs/avalanchego/api/proto/greadcloserproto" "github.com/ava-labs/avalanchego/api/proto/gresponsewriterproto" "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/greadcloser" "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter" "github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils" ) @@ -37,40 +36,43 @@ func NewClient(client ghttpproto.HTTPClient, broker *plugin.GRPCBroker) *Client } func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // rfc2616#section-14.42: The Upgrade general-header allows the client + // to specify a communication protocols it supports and would like to + // use. Upgrade (e.g. websockets) is a more expensive transaction and + // if not required use the less expensive HTTPSimple. + if !isUpgradeRequest(r) { + c.serveHTTPSimple(w, r) + return + } + closer := grpcutils.ServerCloser{} defer closer.GracefulStop() // Wrap [w] with a lock to ensure that it is accessed in a thread-safe manner. w = gresponsewriter.NewLockedWriter(w) - readerID := c.broker.NextId() - go c.broker.AcceptAndServe(readerID, func(opts []grpc.ServerOption) *grpc.Server { + readerWriterID := c.broker.NextId() + // Start responsewriter gRPC service. + go c.broker.AcceptAndServe(readerWriterID, func(opts []grpc.ServerOption) *grpc.Server { opts = append(opts, grpc.MaxRecvMsgSize(math.MaxInt), grpc.MaxSendMsgSize(math.MaxInt), ) - reader := grpc.NewServer(opts...) - closer.Add(reader) - greadcloserproto.RegisterReaderServer(reader, greadcloser.NewServer(r.Body)) - - return reader + server := grpc.NewServer(opts...) + closer.Add(server) + gresponsewriterproto.RegisterWriterServer(server, gresponsewriter.NewServer(w, c.broker)) + return server }) - writerID := c.broker.NextId() - go c.broker.AcceptAndServe(writerID, func(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, - grpc.MaxRecvMsgSize(math.MaxInt), - grpc.MaxSendMsgSize(math.MaxInt), - ) - writer := grpc.NewServer(opts...) - closer.Add(writer) - gresponsewriterproto.RegisterWriterServer(writer, gresponsewriter.NewServer(w, c.broker)) - return writer - }) + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } req := &ghttpproto.HTTPRequest{ ResponseWriter: &ghttpproto.ResponseWriter{ - Id: writerID, + Id: readerWriterID, Header: make([]*ghttpproto.Element, 0, len(r.Header)), }, Request: &ghttpproto.Request{ @@ -79,7 +81,7 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { ProtoMajor: int32(r.ProtoMajor), ProtoMinor: int32(r.ProtoMinor), Header: make([]*ghttpproto.Element, 0, len(r.Header)), - Body: readerID, + Body: body, ContentLength: r.ContentLength, TransferEncoding: r.TransferEncoding, Host: r.Host, @@ -138,20 +140,18 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.TLS != nil { req.Request.Tls = &ghttpproto.ConnectionState{ - Version: uint32(r.TLS.Version), - HandshakeComplete: r.TLS.HandshakeComplete, - DidResume: r.TLS.DidResume, - CipherSuite: uint32(r.TLS.CipherSuite), - NegotiatedProtocol: r.TLS.NegotiatedProtocol, - NegotiatedProtocolIsMutual: r.TLS.NegotiatedProtocolIsMutual, - ServerName: r.TLS.ServerName, + Version: uint32(r.TLS.Version), + HandshakeComplete: r.TLS.HandshakeComplete, + DidResume: r.TLS.DidResume, + CipherSuite: uint32(r.TLS.CipherSuite), + NegotiatedProtocol: r.TLS.NegotiatedProtocol, + ServerName: r.TLS.ServerName, PeerCertificates: &ghttpproto.Certificates{ Cert: make([][]byte, len(r.TLS.PeerCertificates)), }, VerifiedChains: make([]*ghttpproto.Certificates, len(r.TLS.VerifiedChains)), SignedCertificateTimestamps: r.TLS.SignedCertificateTimestamps, OcspResponse: r.TLS.OCSPResponse, - TlsUnique: r.TLS.TLSUnique, } for i, cert := range r.TLS.PeerCertificates { req.Request.Tls.PeerCertificates.Cert[i] = cert.Raw @@ -166,8 +166,62 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - _, err := c.client.Handle(r.Context(), req) + _, err = c.client.Handle(r.Context(), req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +// serveHTTPSimple converts an http request to a gRPC HTTPRequest and returns the +// response to the client. Protocol upgrade requests (websockets) are not supported +// and should use ServeHTTP. Based on https://www.weave.works/blog/turtles-way-http-grpc. +func (c *Client) serveHTTPSimple(w http.ResponseWriter, r *http.Request) { + req, err := getHTTPSimpleRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + resp, err := c.client.HandleSimple(r.Context(), req) + if err != nil { + // Some errors will actually contain a valid resp, just need to unpack it + var ok bool + resp, ok = grpcutils.GetHTTPResponseFromError(err) + if !ok { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + if err := convertWriteResponse(w, resp); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +// getHTTPSimpleRequest takes an http request as input and returns a gRPC HandleSimpleHTTPRequest. +func getHTTPSimpleRequest(r *http.Request) (*ghttpproto.HandleSimpleHTTPRequest, error) { + body, err := io.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + return nil, err } + return &ghttpproto.HandleSimpleHTTPRequest{ + Method: r.Method, + Url: r.RequestURI, + Body: body, + Headers: grpcutils.GetHTTPHeader(r.Header), + }, nil +} + +// convertWriteResponse converts a gRPC HandleSimpleHTTPResponse to an HTTP response. +func convertWriteResponse(w http.ResponseWriter, resp *ghttpproto.HandleSimpleHTTPResponse) error { + grpcutils.MergeHTTPHeader(resp.Headers, w.Header()) + w.WriteHeader(int(resp.Code)) + _, err := w.Write(resp.Body) + return err +} + +// isUpgradeRequest returns true if the upgrade key exists in header and value is non empty. +func isUpgradeRequest(req *http.Request) bool { + return req.Header.Get("Upgrade") != "" } diff --git a/vms/rpcchainvm/ghttp/http_server.go b/vms/rpcchainvm/ghttp/http_server.go index ce9992613f02..8eaba5ee351a 100644 --- a/vms/rpcchainvm/ghttp/http_server.go +++ b/vms/rpcchainvm/ghttp/http_server.go @@ -4,23 +4,27 @@ package ghttp import ( + "bytes" "context" "crypto/tls" "crypto/x509" "net/http" "net/url" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/hashicorp/go-plugin" "github.com/ava-labs/avalanchego/api/proto/ghttpproto" - "github.com/ava-labs/avalanchego/api/proto/greadcloserproto" "github.com/ava-labs/avalanchego/api/proto/gresponsewriterproto" - "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/greadcloser" "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter" + "github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils" ) -var _ ghttpproto.HTTPServer = &Server{} +var ( + _ ghttpproto.HTTPServer = &Server{} + _ http.ResponseWriter = &ResponseWriter{} +) // Server is an http.Handler that is managed over RPC. type Server struct { @@ -37,34 +41,25 @@ func NewServer(handler http.Handler, broker *plugin.GRPCBroker) *Server { } } -func (s *Server) Handle(ctx context.Context, req *ghttpproto.HTTPRequest) (*ghttpproto.HTTPResponse, error) { - writerConn, err := s.broker.Dial(req.ResponseWriter.Id) +func (s *Server) Handle(ctx context.Context, req *ghttpproto.HTTPRequest) (*emptypb.Empty, error) { + readWriteConn, err := s.broker.Dial(req.ResponseWriter.Id) if err != nil { return nil, err } - readerConn, err := s.broker.Dial(req.Request.Body) - if err != nil { - // Drop any error that occurs during closing to return the original - // error. - _ = writerConn.Close() - return nil, err - } - writerHeaders := make(http.Header) for _, elem := range req.ResponseWriter.Header { writerHeaders[elem.Key] = elem.Values } - writer := gresponsewriter.NewClient(writerHeaders, gresponsewriterproto.NewWriterClient(writerConn), s.broker) - reader := greadcloser.NewClient(greadcloserproto.NewReaderClient(readerConn)) + writer := gresponsewriter.NewClient(writerHeaders, gresponsewriterproto.NewWriterClient(readWriteConn), s.broker) // create the request with the current context request, err := http.NewRequestWithContext( ctx, req.Request.Method, req.Request.RequestUri, - reader, + bytes.NewBuffer(req.Request.Body), ) if err != nil { return nil, err @@ -119,13 +114,12 @@ func (s *Server) Handle(ctx context.Context, req *ghttpproto.HTTPRequest) (*ghtt DidResume: req.Request.Tls.DidResume, CipherSuite: uint16(req.Request.Tls.CipherSuite), NegotiatedProtocol: req.Request.Tls.NegotiatedProtocol, - NegotiatedProtocolIsMutual: req.Request.Tls.NegotiatedProtocolIsMutual, + NegotiatedProtocolIsMutual: true, // always true per https://pkg.go.dev/crypto/tls#ConnectionState ServerName: req.Request.Tls.ServerName, PeerCertificates: make([]*x509.Certificate, len(req.Request.Tls.PeerCertificates.Cert)), VerifiedChains: make([][]*x509.Certificate, len(req.Request.Tls.VerifiedChains)), SignedCertificateTimestamps: req.Request.Tls.SignedCertificateTimestamps, OCSPResponse: req.Request.Tls.OcspResponse, - TLSUnique: req.Request.Tls.TlsUnique, } for i, certBytes := range req.Request.Tls.PeerCertificates.Cert { cert, err := x509.ParseCertificate(certBytes) @@ -148,10 +142,70 @@ func (s *Server) Handle(ctx context.Context, req *ghttpproto.HTTPRequest) (*ghtt s.handler.ServeHTTP(writer, request) - errs := wrappers.Errs{} - errs.Add( - writerConn.Close(), - readerConn.Close(), - ) - return &ghttpproto.HTTPResponse{}, errs.Err + return &emptypb.Empty{}, readWriteConn.Close() +} + +// HandleSimple handles http requests over http2 using a simple request response model. +// Websockets are not supported. Based on https://www.weave.works/blog/turtles-way-http-grpc/ +func (s *Server) HandleSimple(ctx context.Context, r *ghttpproto.HandleSimpleHTTPRequest) (*ghttpproto.HandleSimpleHTTPResponse, error) { + req, err := http.NewRequest(r.Method, r.Url, bytes.NewBuffer(r.Body)) + if err != nil { + return nil, err + } + + grpcutils.MergeHTTPHeader(r.Headers, req.Header) + + req = req.WithContext(ctx) + req.RequestURI = r.Url + req.ContentLength = int64(len(r.Body)) + + w := newResponseWriter() + s.handler.ServeHTTP(w, req) + + resp := &ghttpproto.HandleSimpleHTTPResponse{ + Code: int32(w.statusCode), + Headers: grpcutils.GetHTTPHeader(w.Header()), + Body: w.body.Bytes(), + } + + if w.statusCode == http.StatusInternalServerError { + return nil, grpcutils.GetGRPCErrorFromHTTPResponse(resp) + } + return resp, nil +} + +type ResponseWriter struct { + body *bytes.Buffer + header http.Header + statusCode int +} + +// newResponseWriter returns very basic implementation of the http.ResponseWriter +func newResponseWriter() *ResponseWriter { + return &ResponseWriter{ + body: new(bytes.Buffer), + header: make(http.Header), + statusCode: http.StatusOK, + } +} + +func (w *ResponseWriter) Header() http.Header { + return w.header +} + +func (w *ResponseWriter) Write(buf []byte) (int, error) { + w.body.Write(buf) + return len(buf), nil +} + +func (w *ResponseWriter) WriteHeader(code int) { + w.statusCode = code +} + +func (w *ResponseWriter) StatusCode() int { + return w.statusCode +} + +func (w *ResponseWriter) Body() *bytes.Buffer { + return w.body } diff --git a/vms/rpcchainvm/grpcutils/util.go b/vms/rpcchainvm/grpcutils/util.go new file mode 100644 index 000000000000..61814c472cda --- /dev/null +++ b/vms/rpcchainvm/grpcutils/util.go @@ -0,0 +1,78 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package grpcutils + +import ( + "fmt" + "net/http" + + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + spb "google.golang.org/genproto/googleapis/rpc/status" + + "github.com/ava-labs/avalanchego/api/proto/ghttpproto" +) + +func Errorf(code int, tmpl string, args ...interface{}) error { + return GetGRPCErrorFromHTTPResponse(&ghttpproto.HandleSimpleHTTPResponse{ + Code: int32(code), + Body: []byte(fmt.Sprintf(tmpl, args...)), + }) +} + +// GetGRPCErrorFromHTTPRespone takes an HandleSimpleHTTPResponse as input and returns a gRPC error. +func GetGRPCErrorFromHTTPResponse(resp *ghttpproto.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) (*ghttpproto.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 ghttpproto.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) []*ghttpproto.Element { + result := make([]*ghttpproto.Element, 0, len(hs)) + for k, vs := range hs { + result = append(result, &ghttpproto.Element{ + Key: k, + Values: vs, + }) + } + return result +} + +// MergeHTTPHeader takes a slice of Header and merges with http.Header map. +func MergeHTTPHeader(hs []*ghttpproto.Element, header http.Header) { + for _, h := range hs { + header[h.Key] = h.Values + } +} diff --git a/vms/rpcchainvm/plugin_test.go b/vms/rpcchainvm/plugin_test.go new file mode 100644 index 000000000000..4caf4d2054de --- /dev/null +++ b/vms/rpcchainvm/plugin_test.go @@ -0,0 +1,104 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package rpcchainvm + +import ( + "context" + "fmt" + "os" + "os/exec" + "testing" + + hclog "github.com/hashicorp/go-hclog" + plugin "github.com/hashicorp/go-plugin" + + "google.golang.org/grpc" + + "github.com/ava-labs/avalanchego/api/proto/vmproto" +) + +var ( + TestHandshake = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "VM_PLUGIN", + MagicCookieValue: "dynamic", + } + + TestPluginMap = map[string]plugin.Plugin{ + "vm": &testVMPlugin{}, + } + + _ plugin.Plugin = &testVMPlugin{} + _ plugin.GRPCPlugin = &testVMPlugin{} +) + +// helperProcess helps with creating the plugin binary for testing. +func helperProcess(s ...string) *exec.Cmd { + cs := []string{"-test.run=TestHelperProcess", "--"} + cs = append(cs, s...) + env := []string{ + "TEST_PROCESS=1", + } + run := os.Args[0] + cmd := exec.Command(run, cs...) + env = append(env, os.Environ()...) + cmd.Env = env + return cmd +} + +func TestHelperProcess(*testing.T) { + if os.Getenv("TEST_PROCESS") != "1" { + return + } + + args := os.Args + for len(args) > 0 { + if args[0] == "--" { + args = args[1:] + break + } + + args = args[1:] + } + + if len(args) == 0 { + fmt.Fprintf(os.Stderr, "failed to receive command\n") + os.Exit(2) + } + + pluginLogger := hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: true, + }) + + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: TestHandshake, + Plugins: map[string]plugin.Plugin{ + "vm": NewTestVM(&TestSubnetVM{logger: pluginLogger}), + }, + + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) + os.Exit(0) +} + +type testVMPlugin struct { + plugin.NetRPCUnsupportedPlugin + vm TestVM +} + +func NewTestVM(vm *TestSubnetVM) plugin.Plugin { + return &testVMPlugin{vm: vm} +} + +func (p *testVMPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + vmproto.RegisterVMServer(s, NewTestServer(p.vm, broker)) + return nil +} + +func (p *testVMPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return NewTestClient(vmproto.NewVMClient(c), broker), nil +} diff --git a/vms/rpcchainvm/vm.go b/vms/rpcchainvm/vm.go index 7357ab6b8f1e..af412b1dfc95 100644 --- a/vms/rpcchainvm/vm.go +++ b/vms/rpcchainvm/vm.go @@ -17,7 +17,7 @@ import ( var ( // Handshake is a common handshake that is shared by plugin and host. Handshake = plugin.HandshakeConfig{ - ProtocolVersion: 10, + ProtocolVersion: 11, MagicCookieKey: "VM_PLUGIN", MagicCookieValue: "dynamic", } diff --git a/vms/rpcchainvm/vm_client.go b/vms/rpcchainvm/vm_client.go index 39d711a47bdf..dda7e206bd25 100644 --- a/vms/rpcchainvm/vm_client.go +++ b/vms/rpcchainvm/vm_client.go @@ -132,47 +132,22 @@ func (vm *VMClient) Initialize( vm.snLookup = gsubnetlookup.NewServer(ctx.SNLookup) vm.appSender = appsender.NewServer(appSender) - // start the messenger server - messengerBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(messengerBrokerID, vm.startMessengerServer) - - // start the keystore server - keystoreBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(keystoreBrokerID, vm.startKeystoreServer) - - // start the shared memory server - sharedMemoryBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(sharedMemoryBrokerID, vm.startSharedMemoryServer) - - // start the blockchain alias server - bcLookupBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(bcLookupBrokerID, vm.startBCLookupServer) - - // start the subnet alias server - snLookupBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(snLookupBrokerID, vm.startSNLookupServer) - - // start the AppSender server - appSenderBrokerID := vm.broker.NextId() - go vm.broker.AcceptAndServe(appSenderBrokerID, vm.startAppSenderServer) + // start the gRPC init server + initServerID := vm.broker.NextId() + go vm.broker.AcceptAndServe(initServerID, vm.startInitServer) resp, err := vm.client.Initialize(context.Background(), &vmproto.InitializeRequest{ - NetworkId: ctx.NetworkID, - SubnetId: ctx.SubnetID[:], - ChainId: ctx.ChainID[:], - NodeId: ctx.NodeID.Bytes(), - XChainId: ctx.XChainID[:], - AvaxAssetId: ctx.AVAXAssetID[:], - GenesisBytes: genesisBytes, - UpgradeBytes: upgradeBytes, - ConfigBytes: configBytes, - DbServers: versionedDBServers, - EngineServer: messengerBrokerID, - KeystoreServer: keystoreBrokerID, - SharedMemoryServer: sharedMemoryBrokerID, - BcLookupServer: bcLookupBrokerID, - SnLookupServer: snLookupBrokerID, - AppSenderServer: appSenderBrokerID, + NetworkId: ctx.NetworkID, + SubnetId: ctx.SubnetID[:], + ChainId: ctx.ChainID[:], + NodeId: ctx.NodeID.Bytes(), + XChainId: ctx.XChainID[:], + AvaxAssetId: ctx.AVAXAssetID[:], + GenesisBytes: genesisBytes, + UpgradeBytes: upgradeBytes, + ConfigBytes: configBytes, + DbServers: versionedDBServers, + InitServer: initServerID, }) if err != nil { return err @@ -242,56 +217,31 @@ func (vm *VMClient) startDBServerFunc(db rpcdbproto.DatabaseServer) func(opts [] opts = append(opts, serverOptions...) server := grpc.NewServer(opts...) vm.serverCloser.Add(server) + rpcdbproto.RegisterDatabaseServer(server, db) + return server } } -func (vm *VMClient) startMessengerServer(opts []grpc.ServerOption) *grpc.Server { +func (vm *VMClient) startInitServer(opts []grpc.ServerOption) *grpc.Server { opts = append(opts, serverOptions...) server := grpc.NewServer(opts...) vm.serverCloser.Add(server) - messengerproto.RegisterMessengerServer(server, vm.messenger) - return server -} -func (vm *VMClient) startKeystoreServer(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, serverOptions...) - server := grpc.NewServer(opts...) - vm.serverCloser.Add(server) + // register the messenger service + messengerproto.RegisterMessengerServer(server, vm.messenger) + // register the keystore service gkeystoreproto.RegisterKeystoreServer(server, vm.keystore) - return server -} - -func (vm *VMClient) startSharedMemoryServer(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, serverOptions...) - server := grpc.NewServer(opts...) - vm.serverCloser.Add(server) + // register the shared memory service gsharedmemoryproto.RegisterSharedMemoryServer(server, vm.sharedMemory) - return server -} - -func (vm *VMClient) startBCLookupServer(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, serverOptions...) - server := grpc.NewServer(opts...) - vm.serverCloser.Add(server) + // register the blockchain alias service galiasreaderproto.RegisterAliasReaderServer(server, vm.bcLookup) - return server -} - -func (vm *VMClient) startSNLookupServer(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, serverOptions...) - server := grpc.NewServer(opts...) - vm.serverCloser.Add(server) + // register the subnet alias service gsubnetlookupproto.RegisterSubnetLookupServer(server, vm.snLookup) - return server -} - -func (vm *VMClient) startAppSenderServer(opts []grpc.ServerOption) *grpc.Server { - opts = append(opts, serverOptions...) - server := grpc.NewServer(opts...) - vm.serverCloser.Add(server) + // register the AppSender service appsenderproto.RegisterAppSenderServer(server, vm.appSender) + return server } diff --git a/vms/rpcchainvm/vm_server.go b/vms/rpcchainvm/vm_server.go index c8d779fa0cae..7c71823d6f06 100644 --- a/vms/rpcchainvm/vm_server.go +++ b/vms/rpcchainvm/vm_server.go @@ -124,60 +124,20 @@ func (vm *VMServer) Initialize(_ context.Context, req *vmproto.InitializeRequest return nil, err } - msgConn, err := vm.broker.Dial(req.EngineServer) + clientConn, err := vm.broker.Dial(req.InitServer) if err != nil { // Ignore closing errors to return the original error _ = vm.connCloser.Close() return nil, err } - vm.connCloser.Add(msgConn) + vm.connCloser.Add(clientConn) - keystoreConn, err := vm.broker.Dial(req.KeystoreServer) - if err != nil { - // Ignore closing error to return the original error - _ = vm.connCloser.Close() - return nil, err - } - vm.connCloser.Add(keystoreConn) - - sharedMemoryConn, err := vm.broker.Dial(req.SharedMemoryServer) - if err != nil { - // Ignore closing error to return the original error - _ = vm.connCloser.Close() - return nil, err - } - vm.connCloser.Add(sharedMemoryConn) - - bcLookupConn, err := vm.broker.Dial(req.BcLookupServer) - if err != nil { - // Ignore closing error to return the original error - _ = vm.connCloser.Close() - return nil, err - } - vm.connCloser.Add(bcLookupConn) - - snLookupConn, err := vm.broker.Dial(req.SnLookupServer) - if err != nil { - // Ignore closing error to return the original error - _ = vm.connCloser.Close() - return nil, err - } - vm.connCloser.Add(snLookupConn) - - appSenderConn, err := vm.broker.Dial(req.AppSenderServer) - if err != nil { - // Ignore closing error to return the original error - _ = vm.connCloser.Close() - return nil, err - } - vm.connCloser.Add(appSenderConn) - - msgClient := messenger.NewClient(messengerproto.NewMessengerClient(msgConn)) - keystoreClient := gkeystore.NewClient(gkeystoreproto.NewKeystoreClient(keystoreConn), vm.broker) - sharedMemoryClient := gsharedmemory.NewClient(gsharedmemoryproto.NewSharedMemoryClient(sharedMemoryConn)) - bcLookupClient := galiasreader.NewClient(galiasreaderproto.NewAliasReaderClient(bcLookupConn)) - snLookupClient := gsubnetlookup.NewClient(gsubnetlookupproto.NewSubnetLookupClient(snLookupConn)) - appSenderClient := appsender.NewClient(appsenderproto.NewAppSenderClient(appSenderConn)) + msgClient := messenger.NewClient(messengerproto.NewMessengerClient(clientConn)) + keystoreClient := gkeystore.NewClient(gkeystoreproto.NewKeystoreClient(clientConn), vm.broker) + sharedMemoryClient := gsharedmemory.NewClient(gsharedmemoryproto.NewSharedMemoryClient(clientConn)) + bcLookupClient := galiasreader.NewClient(galiasreaderproto.NewAliasReaderClient(clientConn)) + snLookupClient := gsubnetlookup.NewClient(gsubnetlookupproto.NewSubnetLookupClient(clientConn)) + appSenderClient := appsender.NewClient(appsenderproto.NewAppSenderClient(clientConn)) toEngine := make(chan common.Message, 1) vm.closed = make(chan struct{}) diff --git a/vms/rpcchainvm/vm_test.go b/vms/rpcchainvm/vm_test.go new file mode 100644 index 000000000000..d5972b0f0b74 --- /dev/null +++ b/vms/rpcchainvm/vm_test.go @@ -0,0 +1,353 @@ +// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package rpcchainvm + +import ( + "bytes" + "context" + j "encoding/json" + "fmt" + "io" + "net" + "net/http" + "testing" + + "github.com/gorilla/mux" + "github.com/gorilla/websocket" + + gorillarpc "github.com/gorilla/rpc/v2" + + hclog "github.com/hashicorp/go-hclog" + plugin "github.com/hashicorp/go-plugin" + + "github.com/stretchr/testify/assert" + + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/ava-labs/avalanchego/api/proto/ghttpproto" + "github.com/ava-labs/avalanchego/api/proto/vmproto" + "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp" + "github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils" + + cjson "github.com/ava-labs/avalanchego/utils/json" +) + +// Test_VMCreateHandlers tests the Handle and HandleSimple RPCs by creating a plugin and +// serving the handlers exposed by the subnet. The test then will exercise the service +// as a regression test. +func Test_VMCreateHandlers(t *testing.T) { + assert := assert.New(t) + pr := &pingRequest{ + Version: "2.0", + Method: "subnet.ping", + Params: []string{}, + ID: "1", + } + pingBody, err := j.Marshal(pr) + assert.NoError(err) + + scenarios := []struct { + name string + payload []byte + }{ + { + name: "test HTTP gRPC service", + payload: pingBody, + }, + } + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + process := helperProcess("vm") + c := plugin.NewClient(&plugin.ClientConfig{ + Cmd: process, + HandshakeConfig: TestHandshake, + Plugins: TestPluginMap, + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + }) + defer c.Kill() + + _, err := c.Start() + assert.NoErrorf(err, "failed to start plugin: %v", err) + + if v := c.Protocol(); v != plugin.ProtocolGRPC { + assert.NoErrorf(err, "invalid protocol %q: :%v", c.Protocol(), err) + } + + // Get the plugin client. + client, err := c.Client() + assert.NoErrorf(err, "failed to get plugin client: %v", err) + + // Grab the vm implementation. + raw, err := client.Dispense("vm") + assert.NoErrorf(err, "failed to dispense plugin: %v", err) + + // Get vm client. + vm, ok := raw.(*TestVMClient) + if !ok { + assert.NoError(err) + } + + // Get the handlers exposed by the subnet vm. + handlers, err := vm.CreateHandlers() + assert.NoErrorf(err, "failed to get handlers: %v", err) + + r := mux.NewRouter() + for ep, handler := range handlers { + r.Handle(ep, handler.Handler) + } + listener, err := net.Listen("tcp", "localhost:0") + assert.NoErrorf(err, "failed to create listener: %v", err) + + go func() { + err := http.Serve(listener, r) + assert.NoErrorf(err, "failed to serve HTTP: %v", err) + }() + + target := listener.Addr().String() + + for endpoint := range handlers { + switch endpoint { + case "/rpc": + err := testHTTPPingRequest(target, endpoint, scenario.payload) + assert.NoErrorf(err, "%s rpc ping failed: %v", endpoint, err) + + case "/ws": + // expected number of msg echos to receive from websocket server. + // This test is sanity for conn hijack and server push. + expectedMsgCount := 5 + err := testWebsocketEchoRequest(target, endpoint, expectedMsgCount, scenario.payload) + assert.NoErrorf(err, "%s websocket echo failed: %v", endpoint, err) + default: + t.Fatal("unknown handler") + } + } + }) + } +} + +func testHTTPPingRequest(target, endpoint string, payload []byte) error { + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s%s", target, endpoint), bytes.NewBuffer(payload)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + httpClient := new(http.Client) + resp, err := httpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to dial test server: %v", err) + } + defer resp.Body.Close() + + pb, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + var ping testResult + err = j.Unmarshal(pb, &ping) + if err != nil { + return err + } + + if !ping.Result.Success { + return fmt.Errorf("want ping success: true: got %v", ping.Result.Success) + } + return nil +} + +func testWebsocketEchoRequest(target, endpoint string, expectedMsgCount int, payload []byte) error { + dialTarget := fmt.Sprintf("ws://%s%s", target, endpoint) + cli, _, err := websocket.DefaultDialer.Dial(dialTarget, nil) //nolint + if err != nil { + return err + } + defer cli.Close() + + err = cli.WriteMessage(websocket.TextMessage, payload) + if err != nil { + return err + } + + i := 0 + for i < expectedMsgCount { + i++ + // TODO: verify message body + _, _, err := cli.ReadMessage() + if err != nil { + return err + } + } + + // TODO more robust test... + if i != expectedMsgCount { + return fmt.Errorf("want (%d) messages got (%d)", expectedMsgCount, i) + } + return nil +} + +type TestVM interface { + CreateHandlers() (map[string]*common.HTTPHandler, error) +} + +func NewTestServer(vm TestVM, broker *plugin.GRPCBroker) *TestVMServer { + return &TestVMServer{ + vm: vm, + broker: broker, + } +} + +type TestVMServer struct { + vmproto.UnimplementedVMServer + vm TestVM + broker *plugin.GRPCBroker + + serverCloser grpcutils.ServerCloser +} + +func (vm *TestVMServer) CreateHandlers(context.Context, *emptypb.Empty) (*vmproto.CreateHandlersResponse, error) { + handlers, err := vm.vm.CreateHandlers() + if err != nil { + return nil, err + } + + resp := &vmproto.CreateHandlersResponse{} + for prefix, h := range handlers { + handler := h + + serverID := vm.broker.NextId() + go vm.broker.AcceptAndServe(serverID, func(opts []grpc.ServerOption) *grpc.Server { + opts = append(opts, serverOptions...) + server := grpc.NewServer(opts...) + vm.serverCloser.Add(server) + ghttpproto.RegisterHTTPServer(server, ghttp.NewServer(handler.Handler, vm.broker)) + return server + }) + + resp.Handlers = append(resp.Handlers, &vmproto.Handler{ + Prefix: prefix, + LockOptions: uint32(handler.LockOptions), + Server: serverID, + }) + } + return resp, nil +} + +type TestVMClient struct { + client vmproto.VMClient + broker *plugin.GRPCBroker + + conns []*grpc.ClientConn +} + +func NewTestClient(client vmproto.VMClient, broker *plugin.GRPCBroker) *TestVMClient { + return &TestVMClient{ + client: client, + broker: broker, + } +} + +func (vm *TestVMClient) CreateHandlers() (map[string]*common.HTTPHandler, error) { + resp, err := vm.client.CreateHandlers(context.Background(), &emptypb.Empty{}) + if err != nil { + return nil, err + } + + handlers := make(map[string]*common.HTTPHandler, len(resp.Handlers)) + for _, handler := range resp.Handlers { + conn, err := vm.broker.Dial(handler.Server) + if err != nil { + return nil, err + } + + vm.conns = append(vm.conns, conn) + handlers[handler.Prefix] = &common.HTTPHandler{ + LockOptions: common.LockOption(handler.LockOptions), + Handler: ghttp.NewClient(ghttpproto.NewHTTPClient(conn), vm.broker), + } + } + return handlers, nil +} + +type TestSubnetVM struct { + logger hclog.Logger +} + +func (vm *TestSubnetVM) CreateHandlers() (map[string]*common.HTTPHandler, error) { + apis := make(map[string]*common.HTTPHandler) + + testEchoMsgCount := 5 + apis["/ws"] = &common.HTTPHandler{ + LockOptions: common.NoLock, Handler: websocketEchoHandler(testEchoMsgCount), + } + rpcServer, err := getTestRPCServer() + if err != nil { + return nil, err + } + + apis["/rpc"] = &common.HTTPHandler{ + LockOptions: common.NoLock, Handler: rpcServer, + } + return apis, nil +} + +type PingService struct{} + +type PingReply struct { + Success bool `json:"success"` +} + +type pingRequest struct { + Version string `json:"jsonrpc"` + Method string `json:"method"` + Params []string `json:"params"` + ID string `json:"id"` +} + +type testResult struct { + Result PingReply `json:"result"` +} + +func (p *PingService) Ping(_ *http.Request, _ *struct{}, reply *PingReply) (err error) { + reply.Success = true + return nil +} + +func getTestRPCServer() (*gorillarpc.Server, error) { + server := gorillarpc.NewServer() + server.RegisterCodec(cjson.NewCodec(), "application/json") + server.RegisterCodec(cjson.NewCodec(), "application/json;charset=UTF-8") + if err := server.RegisterService(&PingService{}, "subnet"); err != nil { + return nil, fmt.Errorf("failed to create rpc server %v", err) + } + return server, nil +} + +// websocketEchoHandler upgrades the request and sends back N(msgCount) +// echos. +func websocketEchoHandler(msgCount int) http.Handler { + upgrader := websocket.Upgrader{} // use default options + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + defer c.Close() + + mt, b, err := c.ReadMessage() + if err != nil { + if err != io.EOF { + return + } + return + } + for i := 0; i < msgCount; i++ { + err = c.WriteMessage(mt, b) + if err != nil { + return + } + } + }) +}