Merge pull request #20 from lbryio/feature/6/jeffreypicard/hub-federation

Most of federation is written, need to finish udp and test
This commit is contained in:
Jeffrey Picard 2021-11-05 11:14:05 -04:00 committed by GitHub
commit 284f825d22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 2047 additions and 497 deletions

View file

@ -1,4 +1,4 @@
name: 'Build Hub'
name: 'Build and Test Hub'
on:
push:
@ -10,4 +10,5 @@ jobs:
- uses: actions/setup-go@v2.1.3
with:
go-version: 1.16.5
- run: go build .
- run: go build .
- run: cd server && go test -v -race

View file

@ -1,4 +1,4 @@
name: 'Build Hub'
name: 'Build and Test Hub'
on:
push:
@ -30,3 +30,4 @@ jobs:
- run: go get github.com/golang/protobuf/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
- run: go build .
- run: ./protobuf/build.sh
- run: cd server && go test -v -race

3
go.mod
View file

@ -8,10 +8,11 @@ require (
github.com/lbryio/lbry.go/v2 v2.7.2-0.20210625145058-2b155597bf57
github.com/olivere/elastic/v7 v7.0.24
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/client_model v0.2.0
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
golang.org/x/text v0.3.6
google.golang.org/genproto v0.0.0-20210524171403-669157292da3 // indirect
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
google.golang.org/protobuf v1.27.1
gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c
)

3
go.sum
View file

@ -313,8 +313,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -22,5 +22,13 @@ var (
Help: "Histogram of query times",
Buckets: HistogramBuckets,
}, []string{"method"})
PeersKnown = promauto.NewGauge(prometheus.GaugeOpts{
Name: "peers_known",
Help: "Number of peers we know about.",
})
PeersSubscribed = promauto.NewGauge(prometheus.GaugeOpts{
Name: "peers_subbed",
Help: "Number of peers that are subscribed to us.",
})
)

192
main.go
View file

@ -4,189 +4,45 @@ import (
"context"
"fmt"
"log"
"net"
"os"
"strings"
"time"
"github.com/akamensky/argparse"
pb "github.com/lbryio/hub/protobuf/go"
"github.com/lbryio/hub/server"
"github.com/lbryio/lbry.go/v2/extras/util"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
defaultHost = "0.0.0.0"
defaultPort = "50051"
defaultEsHost = "http://localhost"
defaultEsIndex = "claims"
defaultEsPort = "9200"
defaultRefreshDelta = 5
defaultCacheTTL = 5
)
func GetEnvironment(data []string, getkeyval func(item string) (key, val string)) map[string]string {
items := make(map[string]string)
for _, item := range data {
key, val := getkeyval(item)
items[key] = val
}
return items
}
func GetEnvironmentStandard() map[string]string {
return GetEnvironment(os.Environ(), func(item string) (key, val string) {
splits := strings.Split(item, "=")
key = splits[0]
val = splits[1]
return
})
}
/*
func makeServeCmd(parser *argparse.Parser) *argparse.Command {
serveCmd := parser.NewCommand("serve", "start the hub server")
host := serveCmd.String("", "rpchost", &argparse.Options{Required: false, Help: "host", Default: defaultHost})
port := serveCmd.String("", "rpcport", &argparse.Options{Required: false, Help: "port", Default: defaultPort})
esHost := serveCmd.String("", "eshost", &argparse.Options{Required: false, Help: "host", Default: defaultEsHost})
esPort := serveCmd.String("", "esport", &argparse.Options{Required: false, Help: "port", Default: defaultEsPort})
dev := serveCmd.Flag("", "dev", &argparse.Options{Required: false, Help: "port", Default: false})
return serveCmd
}
*/
func parseArgs(searchRequest *pb.SearchRequest) *server.Args {
environment := GetEnvironmentStandard()
parser := argparse.NewParser("hub", "hub server and client")
serveCmd := parser.NewCommand("serve", "start the hub server")
searchCmd := parser.NewCommand("search", "claim search")
debug := parser.Flag("", "debug", &argparse.Options{Required: false, Help: "enable debug logging", Default: false})
host := parser.String("", "rpchost", &argparse.Options{Required: false, Help: "RPC host", Default: defaultHost})
port := parser.String("", "rpcport", &argparse.Options{Required: false, Help: "RPC port", Default: defaultPort})
esHost := parser.String("", "eshost", &argparse.Options{Required: false, Help: "elasticsearch host", Default: defaultEsHost})
esPort := parser.String("", "esport", &argparse.Options{Required: false, Help: "elasticsearch port", Default: defaultEsPort})
esIndex := parser.String("", "esindex", &argparse.Options{Required: false, Help: "elasticsearch index name", Default: defaultEsIndex})
refreshDelta := parser.Int("", "refresh-delta", &argparse.Options{Required: false, Help: "elasticsearch index refresh delta in seconds", Default: defaultRefreshDelta})
cacheTTL := parser.Int("", "cachettl", &argparse.Options{Required: false, Help: "Cache TTL in minutes", Default: defaultCacheTTL})
text := parser.String("", "text", &argparse.Options{Required: false, Help: "text query"})
name := parser.String("", "name", &argparse.Options{Required: false, Help: "name"})
claimType := parser.String("", "claim_type", &argparse.Options{Required: false, Help: "claim_type"})
id := parser.String("", "id", &argparse.Options{Required: false, Help: "id"})
author := parser.String("", "author", &argparse.Options{Required: false, Help: "author"})
title := parser.String("", "title", &argparse.Options{Required: false, Help: "title"})
description := parser.String("", "description", &argparse.Options{Required: false, Help: "description"})
channelId := parser.String("", "channel_id", &argparse.Options{Required: false, Help: "channel id"})
channelIds := parser.StringList("", "channel_ids", &argparse.Options{Required: false, Help: "channel ids"})
// Now parse the arguments
err := parser.Parse(os.Args)
if err != nil {
log.Fatalln(parser.Usage(err))
}
args := &server.Args{
CmdType: server.SearchCmd,
Host: *host,
Port: ":" + *port,
EsHost: *esHost,
EsPort: *esPort,
EsIndex: *esIndex,
Debug: *debug,
RefreshDelta: *refreshDelta,
CacheTTL: *cacheTTL,
}
if esHost, ok := environment["ELASTIC_HOST"]; ok {
args.EsHost = esHost
}
if !strings.HasPrefix(args.EsHost, "http") {
args.EsHost = "http://" + args.EsHost
}
if esPort, ok := environment["ELASTIC_PORT"]; ok {
args.EsPort = esPort
}
/*
Verify no invalid argument combinations
*/
if len(*channelIds) > 0 && *channelId != "" {
log.Fatal("Cannot specify both channel_id and channel_ids")
}
if serveCmd.Happened() {
args.CmdType = server.ServeCmd
} else if searchCmd.Happened() {
args.CmdType = server.SearchCmd
}
if *text != "" {
searchRequest.Text = *text
}
if *name != "" {
searchRequest.ClaimName = *name
}
if *claimType != "" {
searchRequest.ClaimType = []string{*claimType}
}
if *id != "" {
searchRequest.ClaimId = &pb.InvertibleField{Invert: false, Value: []string{*id}}
}
if *author != "" {
searchRequest.Author = *author
}
if *title != "" {
searchRequest.Title = *title
}
if *description != "" {
searchRequest.Description = *description
}
if *channelId != "" {
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: []string{*channelId}}
}
if len(*channelIds) > 0 {
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: *channelIds}
}
return args
}
func main() {
ctx := context.Background()
searchRequest := &pb.SearchRequest{}
args := parseArgs(searchRequest)
args := server.ParseArgs(searchRequest)
if args.CmdType == server.ServeCmd {
// This will cancel goroutines with the server finishes.
ctxWCancel, cancel := context.WithCancel(ctx)
defer cancel()
l, err := net.Listen("tcp", args.Port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := server.MakeHubServer(args)
pb.RegisterHubServer(s.GrpcServer, s)
reflection.Register(s.GrpcServer)
log.Printf("listening on %s\n", l.Addr().String())
log.Println(s.Args)
go s.PromethusEndpoint("2112", "metrics")
if err := s.GrpcServer.Serve(l); err != nil {
log.Fatalf("failed to serve: %v", err)
}
s := server.MakeHubServer(ctxWCancel, args)
s.Run()
//l, err := net.Listen("tcp", ":"+args.Port)
//if err != nil {
// log.Fatalf("failed to listen: %v", err)
//}
//
//pb.RegisterHubServer(s.GrpcServer, s)
//reflection.Register(s.GrpcServer)
//
//log.Printf("listening on %s\n", l.Addr().String())
//log.Println(s.Args)
//if err := s.GrpcServer.Serve(l); err != nil {
// log.Fatalf("failed to serve: %v", err)
//}
return
}
conn, err := grpc.Dial("localhost"+args.Port,
conn, err := grpc.Dial("localhost:"+args.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
@ -197,13 +53,13 @@ func main() {
c := pb.NewHubClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
ctxWTimeout, cancelQuery := context.WithTimeout(ctx, time.Second)
defer cancelQuery()
log.Println(args)
switch args.CmdType {
case server.SearchCmd:
r, err := c.Search(ctx, searchRequest)
r, err := c.Search(ctxWTimeout, searchRequest)
if err != nil {
log.Fatal(err)
}

View file

@ -8,6 +8,9 @@ package pb;
service Hub {
rpc Search (SearchRequest) returns (Outputs) {}
rpc Ping (EmptyMessage) returns (StringValue) {}
rpc Hello (HelloMessage) returns (HelloMessage) {}
rpc AddPeer (ServerMessage) returns (StringValue) {}
rpc PeerSubscribe (ServerMessage) returns (StringValue) {}
rpc Version (EmptyMessage) returns (StringValue) {}
rpc Features (EmptyMessage) returns (StringValue) {}
rpc Broadcast(EmptyMessage) returns (UInt32Value) {}
@ -15,6 +18,17 @@ service Hub {
message EmptyMessage {}
message ServerMessage {
string address = 1;
string port = 2;
}
message HelloMessage {
string port = 1;
string host = 2;
repeated ServerMessage servers = 3;
}
message InvertibleField {
bool invert = 1;
repeated string value = 2;

View file

@ -72,7 +72,7 @@ func (x RangeField_Op) Number() protoreflect.EnumNumber {
// Deprecated: Use RangeField_Op.Descriptor instead.
func (RangeField_Op) EnumDescriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{5, 0}
return file_hub_proto_rawDescGZIP(), []int{7, 0}
}
type EmptyMessage struct {
@ -113,6 +113,124 @@ func (*EmptyMessage) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{0}
}
type ServerMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address"`
Port string `protobuf:"bytes,2,opt,name=port,proto3" json:"port"`
}
func (x *ServerMessage) Reset() {
*x = ServerMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerMessage) ProtoMessage() {}
func (x *ServerMessage) ProtoReflect() protoreflect.Message {
mi := &file_hub_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 ServerMessage.ProtoReflect.Descriptor instead.
func (*ServerMessage) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{1}
}
func (x *ServerMessage) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
func (x *ServerMessage) GetPort() string {
if x != nil {
return x.Port
}
return ""
}
type HelloMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Port string `protobuf:"bytes,1,opt,name=port,proto3" json:"port"`
Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host"`
Servers []*ServerMessage `protobuf:"bytes,3,rep,name=servers,proto3" json:"servers"`
}
func (x *HelloMessage) Reset() {
*x = HelloMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloMessage) ProtoMessage() {}
func (x *HelloMessage) ProtoReflect() protoreflect.Message {
mi := &file_hub_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 HelloMessage.ProtoReflect.Descriptor instead.
func (*HelloMessage) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{2}
}
func (x *HelloMessage) GetPort() string {
if x != nil {
return x.Port
}
return ""
}
func (x *HelloMessage) GetHost() string {
if x != nil {
return x.Host
}
return ""
}
func (x *HelloMessage) GetServers() []*ServerMessage {
if x != nil {
return x.Servers
}
return nil
}
type InvertibleField struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -125,7 +243,7 @@ type InvertibleField struct {
func (x *InvertibleField) Reset() {
*x = InvertibleField{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[1]
mi := &file_hub_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -138,7 +256,7 @@ func (x *InvertibleField) String() string {
func (*InvertibleField) ProtoMessage() {}
func (x *InvertibleField) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[1]
mi := &file_hub_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -151,7 +269,7 @@ func (x *InvertibleField) ProtoReflect() protoreflect.Message {
// Deprecated: Use InvertibleField.ProtoReflect.Descriptor instead.
func (*InvertibleField) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{1}
return file_hub_proto_rawDescGZIP(), []int{3}
}
func (x *InvertibleField) GetInvert() bool {
@ -179,7 +297,7 @@ type StringValue struct {
func (x *StringValue) Reset() {
*x = StringValue{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[2]
mi := &file_hub_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -192,7 +310,7 @@ func (x *StringValue) String() string {
func (*StringValue) ProtoMessage() {}
func (x *StringValue) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[2]
mi := &file_hub_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -205,7 +323,7 @@ func (x *StringValue) ProtoReflect() protoreflect.Message {
// Deprecated: Use StringValue.ProtoReflect.Descriptor instead.
func (*StringValue) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{2}
return file_hub_proto_rawDescGZIP(), []int{4}
}
func (x *StringValue) GetValue() string {
@ -226,7 +344,7 @@ type BoolValue struct {
func (x *BoolValue) Reset() {
*x = BoolValue{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[3]
mi := &file_hub_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -239,7 +357,7 @@ func (x *BoolValue) String() string {
func (*BoolValue) ProtoMessage() {}
func (x *BoolValue) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[3]
mi := &file_hub_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -252,7 +370,7 @@ func (x *BoolValue) ProtoReflect() protoreflect.Message {
// Deprecated: Use BoolValue.ProtoReflect.Descriptor instead.
func (*BoolValue) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{3}
return file_hub_proto_rawDescGZIP(), []int{5}
}
func (x *BoolValue) GetValue() bool {
@ -273,7 +391,7 @@ type UInt32Value struct {
func (x *UInt32Value) Reset() {
*x = UInt32Value{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[4]
mi := &file_hub_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -286,7 +404,7 @@ func (x *UInt32Value) String() string {
func (*UInt32Value) ProtoMessage() {}
func (x *UInt32Value) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[4]
mi := &file_hub_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -299,7 +417,7 @@ func (x *UInt32Value) ProtoReflect() protoreflect.Message {
// Deprecated: Use UInt32Value.ProtoReflect.Descriptor instead.
func (*UInt32Value) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{4}
return file_hub_proto_rawDescGZIP(), []int{6}
}
func (x *UInt32Value) GetValue() uint32 {
@ -321,7 +439,7 @@ type RangeField struct {
func (x *RangeField) Reset() {
*x = RangeField{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[5]
mi := &file_hub_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -334,7 +452,7 @@ func (x *RangeField) String() string {
func (*RangeField) ProtoMessage() {}
func (x *RangeField) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[5]
mi := &file_hub_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -347,7 +465,7 @@ func (x *RangeField) ProtoReflect() protoreflect.Message {
// Deprecated: Use RangeField.ProtoReflect.Descriptor instead.
func (*RangeField) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{5}
return file_hub_proto_rawDescGZIP(), []int{7}
}
func (x *RangeField) GetOp() RangeField_Op {
@ -429,7 +547,7 @@ type SearchRequest struct {
func (x *SearchRequest) Reset() {
*x = SearchRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[6]
mi := &file_hub_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -442,7 +560,7 @@ func (x *SearchRequest) String() string {
func (*SearchRequest) ProtoMessage() {}
func (x *SearchRequest) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[6]
mi := &file_hub_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -455,7 +573,7 @@ func (x *SearchRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
func (*SearchRequest) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{6}
return file_hub_proto_rawDescGZIP(), []int{8}
}
func (x *SearchRequest) GetClaimId() *InvertibleField {
@ -848,186 +966,206 @@ var File_hub_proto protoreflect.FileDescriptor
var file_hub_proto_rawDesc = []byte{
0x0a, 0x09, 0x68, 0x75, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a,
0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0e, 0x0a,
0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x3f, 0x0a,
0x0f, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23,
0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x75, 0x0a, 0x0a, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x02, 0x6f, 0x70, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x4f, 0x70, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05,
0x0c, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x3d, 0x0a,
0x0d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18,
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x63, 0x0a, 0x0c,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x68, 0x6f, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x73, 0x22, 0x3f, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46,
0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x2e, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x06, 0x0a, 0x02, 0x45, 0x51, 0x10, 0x00,
0x12, 0x07, 0x0a, 0x03, 0x4c, 0x54, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x54, 0x45,
0x10, 0x02, 0x12, 0x06, 0x0a, 0x02, 0x4c, 0x54, 0x10, 0x03, 0x12, 0x06, 0x0a, 0x02, 0x47, 0x54,
0x10, 0x04, 0x22, 0xe0, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x65,
0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x61,
0x69, 0x6d, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e,
0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x14, 0x0a, 0x05,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d,
0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x05,
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x16, 0x0a,
0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f,
0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69,
0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x31, 0x0a, 0x15,
0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x61, 0x73,
0x74, 0x54, 0x61, 0x6b, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12,
0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27,
0x0a, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69,
0x7a, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x0b, 0x74, 0x78, 0x5f, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x74, 0x78,
0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61,
0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x2c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0d, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d,
0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a,
0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x37, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0e,
0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x3b,
0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x3b, 0x0a, 0x11, 0x65,
0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x31, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65,
0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b,
0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73,
0x68, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x73, 0x68, 0x6f, 0x72, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x6e, 0x6f,
0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a,
0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x17, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a,
0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x19, 0x20, 0x03, 0x28,
0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, 0x0c,
0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1a, 0x20, 0x01,
0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x6c, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x55, 0x49,
0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x75, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x21, 0x0a,
0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x4f, 0x70, 0x52, 0x02, 0x6f, 0x70,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2e, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x06, 0x0a, 0x02,
0x45, 0x51, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x54, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a,
0x03, 0x47, 0x54, 0x45, 0x10, 0x02, 0x12, 0x06, 0x0a, 0x02, 0x4c, 0x54, 0x10, 0x03, 0x12, 0x06,
0x0a, 0x02, 0x47, 0x54, 0x10, 0x04, 0x22, 0xe0, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63,
0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x69,
0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e,
0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52,
0x07, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70,
0x62, 0x2e, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c,
0x64, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04,
0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74,
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52,
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f,
0x62, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42,
0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x73, 0x5f,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0d, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
0x12, 0x31, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x5f, 0x6f, 0x76,
0x65, 0x72, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x61, 0x6b, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x48, 0x65, 0x69,
0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x72,
0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x0b, 0x74,
0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x52, 0x0a, 0x74, 0x78, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x06,
0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x61, 0x6d,
0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x11,
0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x12, 0x26, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c,
0x64, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x37, 0x0a, 0x0f, 0x63, 0x72, 0x65,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x10, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1b,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65,
0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1c,
0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12,
0x2d, 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1d, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x52, 0x09, 0x66, 0x65, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21,
0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x1e,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63,
0x79, 0x12, 0x2a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1f, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a,
0x11, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f,
0x69, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74,
0x65, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x63, 0x65, 0x6e,
0x73, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a,
0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c,
0x61, 0x69, 0x6d, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18,
0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x49, 0x6e, 0x43,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3b, 0x0a, 0x12, 0x69, 0x73, 0x5f,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18,
0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x69, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x10, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74,
0x69, 0x76, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b,
0x6c, 0x64, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67,
0x68, 0x74, 0x12, 0x3b, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x61,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12,
0x3b, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65,
0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x31, 0x0a, 0x0c,
0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x13, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x14, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d,
0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x15, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x72,
0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12,
0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
0x19, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x79, 0x70, 0x65,
0x12, 0x31, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,
0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54,
0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e,
0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x66, 0x65, 0x65, 0x41, 0x6d, 0x6f, 0x75,
0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x63, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x43, 0x75, 0x72,
0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c,
0x61, 0x69, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65,
0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a,
0x0b, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x21, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x52, 0x0a, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a,
0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x73, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x0c, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x52, 0x0f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x6e,
0x74, 0x12, 0x35, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x61, 0x6d, 0x6f,
0x75, 0x6e, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e,
0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x27, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12,
0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x74, 0x78, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6e, 0x6f, 0x75, 0x74, 0x18,
0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33,
0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x74, 0x78, 0x4e, 0x6f, 0x75, 0x74, 0x12, 0x1c,
0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x10,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74,
0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x2f, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65,
0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f,
0x69, 0x64, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6e, 0x79, 0x5f, 0x74, 0x61, 0x67,
0x73, 0x18, 0x31, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6e, 0x79, 0x54, 0x61, 0x67, 0x73,
0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x32, 0x20, 0x03,
0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6e,
0x6f, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x33, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6e,
0x6f, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18,
0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x68, 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2c, 0x0a, 0x0a, 0x68, 0x61,
0x73, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x35, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d,
0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x68,
0x61, 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x36, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x50, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67,
0x65, 0x73, 0x18, 0x37, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6e, 0x79, 0x4c, 0x61, 0x6e,
0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x61,
0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x38, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61,
0x6c, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73,
0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x44, 0x75,
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x5f, 0x74,
0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x54,
0x6f, 0x74, 0x61, 0x6c, 0x73, 0x32, 0xf1, 0x01, 0x0a, 0x03, 0x48, 0x75, 0x62, 0x12, 0x2a, 0x0a,
0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x62, 0x2e,
0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x04, 0x50, 0x69, 0x6e,
0x67, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x08, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72,
0x65, 0x73, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64,
0x63, 0x61, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74,
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x6f, 0x2f, 0x68,
0x75, 0x62, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x2f, 0x70,
0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3b, 0x0a,
0x12, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42,
0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x69, 0x73, 0x53, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x10, 0x65, 0x66,
0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x25,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46,
0x69, 0x65, 0x6c, 0x64, 0x52, 0x0f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e,
0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x27,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46,
0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x63,
0x6f, 0x72, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x2b, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6e,
0x6f, 0x75, 0x74, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55,
0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x74, 0x78, 0x4e, 0x6f,
0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18,
0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x12, 0x29, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x64, 0x69,
0x67, 0x65, 0x73, 0x74, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79,
0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6e, 0x79,
0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x31, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6e, 0x79,
0x54, 0x61, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73,
0x18, 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12,
0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x33, 0x20, 0x03, 0x28,
0x09, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x68, 0x61,
0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x18, 0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x68, 0x61, 0x73, 0x43, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2c,
0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x35, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x52, 0x09, 0x68, 0x61, 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x18,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x70, 0x65, 0x72,
0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x36, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x50, 0x65, 0x72, 0x43, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x6c, 0x61, 0x6e,
0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x37, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6e,
0x79, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c,
0x6c, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x38, 0x20, 0x03, 0x28,
0x09, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12,
0x2b, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09,
0x6e, 0x6f, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52,
0x08, 0x6e, 0x6f, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x32, 0x88, 0x03, 0x0a, 0x03, 0x48, 0x75,
0x62, 0x12, 0x2a, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x11, 0x2e, 0x70, 0x62,
0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b,
0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x00, 0x12, 0x2b, 0x0a,
0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x05, 0x48, 0x65,
0x6c, 0x6c, 0x6f, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x07, 0x41, 0x64, 0x64,
0x50, 0x65, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0d, 0x50, 0x65,
0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x11, 0x2e, 0x70, 0x62,
0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f,
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x00, 0x12, 0x2e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x2e, 0x70,
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f,
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x00, 0x12, 0x2f, 0x0a, 0x08, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x2e,
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a,
0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x22, 0x00, 0x12, 0x30, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12,
0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x6f, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1043,57 +1181,66 @@ func file_hub_proto_rawDescGZIP() []byte {
}
var file_hub_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_hub_proto_goTypes = []interface{}{
(RangeField_Op)(0), // 0: pb.RangeField.Op
(*EmptyMessage)(nil), // 1: pb.EmptyMessage
(*InvertibleField)(nil), // 2: pb.InvertibleField
(*StringValue)(nil), // 3: pb.StringValue
(*BoolValue)(nil), // 4: pb.BoolValue
(*UInt32Value)(nil), // 5: pb.UInt32Value
(*RangeField)(nil), // 6: pb.RangeField
(*SearchRequest)(nil), // 7: pb.SearchRequest
(*Outputs)(nil), // 8: pb.Outputs
(*ServerMessage)(nil), // 2: pb.ServerMessage
(*HelloMessage)(nil), // 3: pb.HelloMessage
(*InvertibleField)(nil), // 4: pb.InvertibleField
(*StringValue)(nil), // 5: pb.StringValue
(*BoolValue)(nil), // 6: pb.BoolValue
(*UInt32Value)(nil), // 7: pb.UInt32Value
(*RangeField)(nil), // 8: pb.RangeField
(*SearchRequest)(nil), // 9: pb.SearchRequest
(*Outputs)(nil), // 10: pb.Outputs
}
var file_hub_proto_depIdxs = []int32{
0, // 0: pb.RangeField.op:type_name -> pb.RangeField.Op
2, // 1: pb.SearchRequest.claim_id:type_name -> pb.InvertibleField
2, // 2: pb.SearchRequest.channel_id:type_name -> pb.InvertibleField
6, // 3: pb.SearchRequest.tx_position:type_name -> pb.RangeField
6, // 4: pb.SearchRequest.amount:type_name -> pb.RangeField
6, // 5: pb.SearchRequest.timestamp:type_name -> pb.RangeField
6, // 6: pb.SearchRequest.creation_timestamp:type_name -> pb.RangeField
6, // 7: pb.SearchRequest.height:type_name -> pb.RangeField
6, // 8: pb.SearchRequest.creation_height:type_name -> pb.RangeField
6, // 9: pb.SearchRequest.activation_height:type_name -> pb.RangeField
6, // 10: pb.SearchRequest.expiration_height:type_name -> pb.RangeField
6, // 11: pb.SearchRequest.release_time:type_name -> pb.RangeField
6, // 12: pb.SearchRequest.repost_count:type_name -> pb.RangeField
6, // 13: pb.SearchRequest.fee_amount:type_name -> pb.RangeField
6, // 14: pb.SearchRequest.duration:type_name -> pb.RangeField
6, // 15: pb.SearchRequest.censor_type:type_name -> pb.RangeField
6, // 16: pb.SearchRequest.channel_join:type_name -> pb.RangeField
4, // 17: pb.SearchRequest.is_signature_valid:type_name -> pb.BoolValue
6, // 18: pb.SearchRequest.effective_amount:type_name -> pb.RangeField
6, // 19: pb.SearchRequest.support_amount:type_name -> pb.RangeField
6, // 20: pb.SearchRequest.trending_score:type_name -> pb.RangeField
5, // 21: pb.SearchRequest.tx_nout:type_name -> pb.UInt32Value
4, // 22: pb.SearchRequest.has_source:type_name -> pb.BoolValue
7, // 23: pb.Hub.Search:input_type -> pb.SearchRequest
1, // 24: pb.Hub.Ping:input_type -> pb.EmptyMessage
1, // 25: pb.Hub.Version:input_type -> pb.EmptyMessage
1, // 26: pb.Hub.Features:input_type -> pb.EmptyMessage
1, // 27: pb.Hub.Broadcast:input_type -> pb.EmptyMessage
8, // 28: pb.Hub.Search:output_type -> pb.Outputs
3, // 29: pb.Hub.Ping:output_type -> pb.StringValue
3, // 30: pb.Hub.Version:output_type -> pb.StringValue
3, // 31: pb.Hub.Features:output_type -> pb.StringValue
5, // 32: pb.Hub.Broadcast:output_type -> pb.UInt32Value
28, // [28:33] is the sub-list for method output_type
23, // [23:28] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
2, // 0: pb.HelloMessage.servers:type_name -> pb.ServerMessage
0, // 1: pb.RangeField.op:type_name -> pb.RangeField.Op
4, // 2: pb.SearchRequest.claim_id:type_name -> pb.InvertibleField
4, // 3: pb.SearchRequest.channel_id:type_name -> pb.InvertibleField
8, // 4: pb.SearchRequest.tx_position:type_name -> pb.RangeField
8, // 5: pb.SearchRequest.amount:type_name -> pb.RangeField
8, // 6: pb.SearchRequest.timestamp:type_name -> pb.RangeField
8, // 7: pb.SearchRequest.creation_timestamp:type_name -> pb.RangeField
8, // 8: pb.SearchRequest.height:type_name -> pb.RangeField
8, // 9: pb.SearchRequest.creation_height:type_name -> pb.RangeField
8, // 10: pb.SearchRequest.activation_height:type_name -> pb.RangeField
8, // 11: pb.SearchRequest.expiration_height:type_name -> pb.RangeField
8, // 12: pb.SearchRequest.release_time:type_name -> pb.RangeField
8, // 13: pb.SearchRequest.repost_count:type_name -> pb.RangeField
8, // 14: pb.SearchRequest.fee_amount:type_name -> pb.RangeField
8, // 15: pb.SearchRequest.duration:type_name -> pb.RangeField
8, // 16: pb.SearchRequest.censor_type:type_name -> pb.RangeField
8, // 17: pb.SearchRequest.channel_join:type_name -> pb.RangeField
6, // 18: pb.SearchRequest.is_signature_valid:type_name -> pb.BoolValue
8, // 19: pb.SearchRequest.effective_amount:type_name -> pb.RangeField
8, // 20: pb.SearchRequest.support_amount:type_name -> pb.RangeField
8, // 21: pb.SearchRequest.trending_score:type_name -> pb.RangeField
7, // 22: pb.SearchRequest.tx_nout:type_name -> pb.UInt32Value
6, // 23: pb.SearchRequest.has_source:type_name -> pb.BoolValue
9, // 24: pb.Hub.Search:input_type -> pb.SearchRequest
1, // 25: pb.Hub.Ping:input_type -> pb.EmptyMessage
3, // 26: pb.Hub.Hello:input_type -> pb.HelloMessage
2, // 27: pb.Hub.AddPeer:input_type -> pb.ServerMessage
2, // 28: pb.Hub.PeerSubscribe:input_type -> pb.ServerMessage
1, // 29: pb.Hub.Version:input_type -> pb.EmptyMessage
1, // 30: pb.Hub.Features:input_type -> pb.EmptyMessage
1, // 31: pb.Hub.Broadcast:input_type -> pb.EmptyMessage
10, // 32: pb.Hub.Search:output_type -> pb.Outputs
5, // 33: pb.Hub.Ping:output_type -> pb.StringValue
3, // 34: pb.Hub.Hello:output_type -> pb.HelloMessage
5, // 35: pb.Hub.AddPeer:output_type -> pb.StringValue
5, // 36: pb.Hub.PeerSubscribe:output_type -> pb.StringValue
5, // 37: pb.Hub.Version:output_type -> pb.StringValue
5, // 38: pb.Hub.Features:output_type -> pb.StringValue
7, // 39: pb.Hub.Broadcast:output_type -> pb.UInt32Value
32, // [32:40] is the sub-list for method output_type
24, // [24:32] is the sub-list for method input_type
24, // [24:24] is the sub-list for extension type_name
24, // [24:24] is the sub-list for extension extendee
0, // [0:24] is the sub-list for field type_name
}
func init() { file_hub_proto_init() }
@ -1116,7 +1263,7 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*InvertibleField); i {
switch v := v.(*ServerMessage); i {
case 0:
return &v.state
case 1:
@ -1128,7 +1275,7 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StringValue); i {
switch v := v.(*HelloMessage); i {
case 0:
return &v.state
case 1:
@ -1140,7 +1287,7 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BoolValue); i {
switch v := v.(*InvertibleField); i {
case 0:
return &v.state
case 1:
@ -1152,7 +1299,7 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UInt32Value); i {
switch v := v.(*StringValue); i {
case 0:
return &v.state
case 1:
@ -1164,7 +1311,7 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RangeField); i {
switch v := v.(*BoolValue); i {
case 0:
return &v.state
case 1:
@ -1176,6 +1323,30 @@ func file_hub_proto_init() {
}
}
file_hub_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UInt32Value); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_hub_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RangeField); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_hub_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SearchRequest); i {
case 0:
return &v.state
@ -1194,7 +1365,7 @@ func file_hub_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_hub_proto_rawDesc,
NumEnums: 1,
NumMessages: 7,
NumMessages: 9,
NumExtensions: 0,
NumServices: 1,
},

View file

@ -20,6 +20,9 @@ const _ = grpc.SupportPackageIsVersion7
type HubClient interface {
Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*Outputs, error)
Ping(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error)
Hello(ctx context.Context, in *HelloMessage, opts ...grpc.CallOption) (*HelloMessage, error)
AddPeer(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (*StringValue, error)
PeerSubscribe(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (*StringValue, error)
Version(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error)
Features(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error)
Broadcast(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*UInt32Value, error)
@ -51,6 +54,33 @@ func (c *hubClient) Ping(ctx context.Context, in *EmptyMessage, opts ...grpc.Cal
return out, nil
}
func (c *hubClient) Hello(ctx context.Context, in *HelloMessage, opts ...grpc.CallOption) (*HelloMessage, error) {
out := new(HelloMessage)
err := c.cc.Invoke(ctx, "/pb.Hub/Hello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *hubClient) AddPeer(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (*StringValue, error) {
out := new(StringValue)
err := c.cc.Invoke(ctx, "/pb.Hub/AddPeer", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *hubClient) PeerSubscribe(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (*StringValue, error) {
out := new(StringValue)
err := c.cc.Invoke(ctx, "/pb.Hub/PeerSubscribe", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *hubClient) Version(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error) {
out := new(StringValue)
err := c.cc.Invoke(ctx, "/pb.Hub/Version", in, out, opts...)
@ -84,6 +114,9 @@ func (c *hubClient) Broadcast(ctx context.Context, in *EmptyMessage, opts ...grp
type HubServer interface {
Search(context.Context, *SearchRequest) (*Outputs, error)
Ping(context.Context, *EmptyMessage) (*StringValue, error)
Hello(context.Context, *HelloMessage) (*HelloMessage, error)
AddPeer(context.Context, *ServerMessage) (*StringValue, error)
PeerSubscribe(context.Context, *ServerMessage) (*StringValue, error)
Version(context.Context, *EmptyMessage) (*StringValue, error)
Features(context.Context, *EmptyMessage) (*StringValue, error)
Broadcast(context.Context, *EmptyMessage) (*UInt32Value, error)
@ -100,6 +133,15 @@ func (UnimplementedHubServer) Search(context.Context, *SearchRequest) (*Outputs,
func (UnimplementedHubServer) Ping(context.Context, *EmptyMessage) (*StringValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
}
func (UnimplementedHubServer) Hello(context.Context, *HelloMessage) (*HelloMessage, error) {
return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented")
}
func (UnimplementedHubServer) AddPeer(context.Context, *ServerMessage) (*StringValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddPeer not implemented")
}
func (UnimplementedHubServer) PeerSubscribe(context.Context, *ServerMessage) (*StringValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method PeerSubscribe not implemented")
}
func (UnimplementedHubServer) Version(context.Context, *EmptyMessage) (*StringValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
}
@ -158,6 +200,60 @@ func _Hub_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{
return interceptor(ctx, in, info, handler)
}
func _Hub_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HubServer).Hello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Hub/Hello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HubServer).Hello(ctx, req.(*HelloMessage))
}
return interceptor(ctx, in, info, handler)
}
func _Hub_AddPeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ServerMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HubServer).AddPeer(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Hub/AddPeer",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HubServer).AddPeer(ctx, req.(*ServerMessage))
}
return interceptor(ctx, in, info, handler)
}
func _Hub_PeerSubscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ServerMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HubServer).PeerSubscribe(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Hub/PeerSubscribe",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HubServer).PeerSubscribe(ctx, req.(*ServerMessage))
}
return interceptor(ctx, in, info, handler)
}
func _Hub_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyMessage)
if err := dec(in); err != nil {
@ -227,6 +323,18 @@ var Hub_ServiceDesc = grpc.ServiceDesc{
MethodName: "Ping",
Handler: _Hub_Ping_Handler,
},
{
MethodName: "Hello",
Handler: _Hub_Hello_Handler,
},
{
MethodName: "AddPeer",
Handler: _Hub_AddPeer_Handler,
},
{
MethodName: "PeerSubscribe",
Handler: _Hub_PeerSubscribe_Handler,
},
{
MethodName: "Version",
Handler: _Hub_Version_Handler,

View file

@ -24,7 +24,11 @@ AREA_RENAME = {
def build_upload_binary(release: github3.repos.release.Release) -> None:
# os.chdir(absolute_path)
os.system("go build .")
# os.system("go build .")
cmd = f'CGO_ENABLED=0 go build -v -ldflags "-X github.com/lbryio/hub/meta.Version={release.name}"'
print(cmd)
# os.system("go build .")
os.system(cmd)
with open("./hub", "rb") as f:
release.upload_asset("binary", "hub", f)
@ -225,9 +229,11 @@ def release(args):
print(body.getvalue())
if unlabeled:
print('The following PRs were skipped and not included in changelog:')
for skipped in unlabeled:
print(skipped)
w('')
print('The following PRs were unlabeled and *will* be included in changelog:')
for notskipped in unlabeled:
print(notskipped)
w(notskipped)
if fixups:
print('The following PRs were marked as fixups and not included in changelog:')

201
server/args.go Normal file
View file

@ -0,0 +1,201 @@
package server
import (
"log"
"os"
"strings"
"github.com/akamensky/argparse"
pb "github.com/lbryio/hub/protobuf/go"
)
const (
ServeCmd = iota
SearchCmd = iota
)
// Args struct contains the arguments to the hub server.
type Args struct {
CmdType int
Host string
Port string
UDPPort string
EsHost string
EsPort string
PrometheusPort string
EsIndex string
RefreshDelta int
CacheTTL int
PeerFile string
Country string
DisableEs bool
Debug bool
LoadPeers bool
StartPrometheus bool
StartUDP bool
WritePeers bool
}
const (
DefaultHost = "0.0.0.0"
DefaultPort = "50051"
DefaultUdpPort = "41119"
DefaultEsHost = "http://localhost"
DefaultEsIndex = "claims"
DefaultEsPort = "9200"
DefaultPrometheusPort = "2112"
DefaultRefreshDelta = 5
DefaultCacheTTL = 5
DefaultPeerFile = "peers.txt"
DefaultCountry = "US"
DefaultLoadPeers = true
DefaultStartPrometheus = true
DefaultStartUDP = true
DefaultWritePeers = true
)
// GetEnvironment takes the environment variables as an array of strings
// and a getkeyval function to turn it into a map.
func GetEnvironment(data []string, getkeyval func(item string) (key, val string)) map[string]string {
items := make(map[string]string)
for _, item := range data {
key, val := getkeyval(item)
items[key] = val
}
return items
}
// GetEnvironmentStandard gets the environment variables as a map.
func GetEnvironmentStandard() map[string]string {
return GetEnvironment(os.Environ(), func(item string) (key, val string) {
splits := strings.Split(item, "=")
key = splits[0]
val = splits[1]
return
})
}
// ParseArgs parses the command line arguments when started the hub server.
func ParseArgs(searchRequest *pb.SearchRequest) *Args {
environment := GetEnvironmentStandard()
parser := argparse.NewParser("hub", "hub server and client")
serveCmd := parser.NewCommand("serve", "start the hub server")
searchCmd := parser.NewCommand("search", "claim search")
host := parser.String("", "rpchost", &argparse.Options{Required: false, Help: "RPC host", Default: DefaultHost})
port := parser.String("", "rpcport", &argparse.Options{Required: false, Help: "RPC port", Default: DefaultPort})
esHost := parser.String("", "eshost", &argparse.Options{Required: false, Help: "elasticsearch host", Default: DefaultEsHost})
esPort := parser.String("", "esport", &argparse.Options{Required: false, Help: "elasticsearch port", Default: DefaultEsPort})
udpPort := parser.String("", "uspport", &argparse.Options{Required: false, Help: "udp ping port", Default: DefaultUdpPort})
prometheusPort := parser.String("", "prometheus-port", &argparse.Options{Required: false, Help: "prometheus port", Default: DefaultPrometheusPort})
esIndex := parser.String("", "esindex", &argparse.Options{Required: false, Help: "elasticsearch index name", Default: DefaultEsIndex})
refreshDelta := parser.Int("", "refresh-delta", &argparse.Options{Required: false, Help: "elasticsearch index refresh delta in seconds", Default: DefaultRefreshDelta})
cacheTTL := parser.Int("", "cachettl", &argparse.Options{Required: false, Help: "Cache TTL in minutes", Default: DefaultCacheTTL})
peerFile := parser.String("", "peerfile", &argparse.Options{Required: false, Help: "Initial peer file for federation", Default: DefaultPeerFile})
country := parser.String("", "country", &argparse.Options{Required: false, Help: "Country this node is running in. Default US.", Default: DefaultCountry})
debug := parser.Flag("", "debug", &argparse.Options{Required: false, Help: "enable debug logging", Default: false})
disableEs := parser.Flag("", "disable-es", &argparse.Options{Required: false, Help: "Disable elastic search, for running/testing independently", Default: false})
loadPeers := parser.Flag("", "load-peers", &argparse.Options{Required: false, Help: "load peers from disk at startup", Default: DefaultLoadPeers})
startPrometheus := parser.Flag("", "start-prometheus", &argparse.Options{Required: false, Help: "Start prometheus server", Default: DefaultStartPrometheus})
startUdp := parser.Flag("", "start-udp", &argparse.Options{Required: false, Help: "Start UDP ping server", Default: DefaultStartUDP})
writePeers := parser.Flag("", "write-peers", &argparse.Options{Required: false, Help: "Write peer to disk as we learn about them", Default: DefaultWritePeers})
text := parser.String("", "text", &argparse.Options{Required: false, Help: "text query"})
name := parser.String("", "name", &argparse.Options{Required: false, Help: "name"})
claimType := parser.String("", "claim_type", &argparse.Options{Required: false, Help: "claim_type"})
id := parser.String("", "id", &argparse.Options{Required: false, Help: "id"})
author := parser.String("", "author", &argparse.Options{Required: false, Help: "author"})
title := parser.String("", "title", &argparse.Options{Required: false, Help: "title"})
description := parser.String("", "description", &argparse.Options{Required: false, Help: "description"})
channelId := parser.String("", "channel_id", &argparse.Options{Required: false, Help: "channel id"})
channelIds := parser.StringList("", "channel_ids", &argparse.Options{Required: false, Help: "channel ids"})
// Now parse the arguments
err := parser.Parse(os.Args)
if err != nil {
log.Fatalln(parser.Usage(err))
}
args := &Args{
CmdType: SearchCmd,
Host: *host,
Port: *port,
EsHost: *esHost,
EsPort: *esPort,
UDPPort: *udpPort,
PrometheusPort: *prometheusPort,
EsIndex: *esIndex,
RefreshDelta: *refreshDelta,
CacheTTL: *cacheTTL,
PeerFile: *peerFile,
Country: *country,
DisableEs: *disableEs,
Debug: *debug,
LoadPeers: *loadPeers,
StartPrometheus: *startPrometheus,
StartUDP: *startUdp,
WritePeers: *writePeers,
}
if esHost, ok := environment["ELASTIC_HOST"]; ok {
args.EsHost = esHost
}
if !strings.HasPrefix(args.EsHost, "http") {
args.EsHost = "http://" + args.EsHost
}
if esPort, ok := environment["ELASTIC_PORT"]; ok {
args.EsPort = esPort
}
if prometheusPort, ok := environment["GOHUB_PROMETHEUS_PORT"]; ok {
args.PrometheusPort = prometheusPort
}
/*
Verify no invalid argument combinations
*/
if len(*channelIds) > 0 && *channelId != "" {
log.Fatal("Cannot specify both channel_id and channel_ids")
}
if serveCmd.Happened() {
args.CmdType = ServeCmd
} else if searchCmd.Happened() {
args.CmdType = SearchCmd
}
if *text != "" {
searchRequest.Text = *text
}
if *name != "" {
searchRequest.ClaimName = *name
}
if *claimType != "" {
searchRequest.ClaimType = []string{*claimType}
}
if *id != "" {
searchRequest.ClaimId = &pb.InvertibleField{Invert: false, Value: []string{*id}}
}
if *author != "" {
searchRequest.Author = *author
}
if *title != "" {
searchRequest.Title = *title
}
if *description != "" {
searchRequest.Description = *description
}
if *channelId != "" {
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: []string{*channelId}}
}
if len(*channelIds) > 0 {
searchRequest.ChannelId = &pb.InvertibleField{Invert: false, Value: *channelIds}
}
return args
}

358
server/federation.go Normal file
View file

@ -0,0 +1,358 @@
package server
import (
"bufio"
"context"
"log"
"os"
"strings"
"sync/atomic"
"time"
"github.com/lbryio/hub/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go"
"google.golang.org/grpc"
)
// FederatedServer hold relevant information about peers that we known about.
type FederatedServer struct {
Address string
Port string
Ts time.Time
}
var (
localHosts = map[string]bool{
"127.0.0.1": true,
"0.0.0.0": true,
"localhost": true,
}
)
// peerKey takes a ServerMessage object and returns the key that for that peer
// in our peer table.
func peerKey(msg *pb.ServerMessage) string {
return msg.Address + ":" + msg.Port
}
// peerKey is a function on a FederatedServer struct to return the key for that
// peer is out peer table.
func (peer *FederatedServer) peerKey() string {
return peer.Address + ":" + peer.Port
}
func (s *Server) incNumPeers() {
atomic.AddInt64(s.NumPeerServers, 1)
}
func (s *Server) decNumPeers() {
atomic.AddInt64(s.NumPeerServers, -1)
}
func (s *Server) getNumPeers() int64 {
return *s.NumPeerServers
}
func (s *Server) incNumSubs() {
atomic.AddInt64(s.NumPeerSubs, 1)
}
func (s *Server) decNumSubs() {
atomic.AddInt64(s.NumPeerSubs, -1)
}
func (s *Server) getNumSubs() int64 {
return *s.NumPeerSubs
}
// loadPeers takes the arguments given to the hub at startup and loads the
// previously known peers from disk and verifies their existence before
// storing them as known peers. Returns a map of peerKey -> object
func (s *Server) loadPeers() error {
peerFile := s.Args.PeerFile
port := s.Args.Port
f, err := os.Open(peerFile)
if err != nil {
log.Println(err)
return err
}
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanLines)
var text []string
for scanner.Scan() {
text = append(text, scanner.Text())
}
err = f.Close()
if err != nil {
log.Println("peer file failed to close: ", err)
}
for _, line := range text {
ipPort := strings.Split(line,":")
if len(ipPort) != 2 {
log.Println("Malformed entry in peer file")
continue
}
// If the peer is us, skip
log.Println(ipPort)
if ipPort[1] == port && localHosts[ipPort[0]] {
log.Println("Self peer, skipping ...")
continue
}
srvMsg := &pb.ServerMessage{
Address: ipPort[0],
Port: ipPort[1],
}
log.Printf("pinging peer %+v\n", srvMsg)
err := s.addPeer(srvMsg, true)
if err != nil {
log.Println(err)
}
}
log.Println("Returning from loadPeers")
return nil
}
// subscribeToPeer subscribes us to a peer to we'll get updates about their
// known peers.
func (s *Server) subscribeToPeer(peer *FederatedServer) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx,
peer.Address+":"+peer.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
return err
}
defer conn.Close()
msg := &pb.ServerMessage{
Address: s.Args.Host,
Port: s.Args.Port,
}
c := pb.NewHubClient(conn)
log.Printf("%s:%s subscribing to %+v\n", s.Args.Host, s.Args.Port, peer)
_, err = c.PeerSubscribe(ctx, msg)
if err != nil {
return err
}
s.Subscribed = true
return nil
}
// helloPeer takes a peer to say hello to and sends a hello message
// containing all the peers we know about and information about us.
// This is used to confirm existence of peers on start and let them
// know about us. Returns the response from the server on success,
// nil otherwise.
func (s *Server) helloPeer(server *FederatedServer) (*pb.HelloMessage, error) {
log.Println("In helloPeer")
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx,
server.Address+":"+server.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Println(err)
return nil, err
}
defer conn.Close()
c := pb.NewHubClient(conn)
msg := &pb.HelloMessage{
Port: s.Args.Port,
Host: s.Args.Host,
Servers: []*pb.ServerMessage{},
}
log.Printf("%s:%s saying hello to %+v\n", s.Args.Host, s.Args.Port, server)
res, err := c.Hello(ctx, msg)
if err != nil {
log.Println(err)
return nil, err
}
log.Println(res)
return res, nil
}
// writePeers writes our current known peers to disk.
func (s *Server) writePeers() {
if !s.Args.WritePeers {
return
}
f, err := os.Create(s.Args.PeerFile)
if err != nil {
log.Println(err)
return
}
writer := bufio.NewWriter(f)
for key, _ := range s.PeerServers {
line := key + "\n"
_, err := writer.WriteString(line)
if err != nil {
log.Println(err)
}
}
err = writer.Flush()
if err != nil {
log.Println(err)
}
err = f.Close()
if err != nil {
log.Println(err)
}
}
// notifyPeer takes a peer to notify and a new peer we just learned about
// and calls AddPeer on the first.
func notifyPeer(peerToNotify *FederatedServer, newPeer *FederatedServer) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx,
peerToNotify.Address+":"+peerToNotify.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
return err
}
defer conn.Close()
msg := &pb.ServerMessage{
Address: newPeer.Address,
Port: newPeer.Port,
}
c := pb.NewHubClient(conn)
_, err = c.AddPeer(ctx, msg)
if err != nil {
return err
}
return nil
}
// notifyPeerSubs takes a new peer server we just learned about and notifies
// all the peers that have subscribed to us about it.
func (s *Server) notifyPeerSubs(newServer *FederatedServer) {
var unsubscribe []string
s.PeerSubsMut.RLock()
for key, peer := range s.PeerSubs {
log.Printf("Notifying peer %s of new node %+v\n", key, newServer)
err := notifyPeer(peer, newServer)
if err != nil {
log.Println("Failed to send data to ", key)
log.Println(err)
unsubscribe = append(unsubscribe, key)
}
}
s.PeerSubsMut.RUnlock()
s.PeerSubsMut.Lock()
for _, key := range unsubscribe {
if _, ok := s.PeerSubs[key]; ok {
delete(s.PeerSubs, key)
s.decNumSubs()
metrics.PeersSubscribed.Dec()
}
}
s.PeerSubsMut.Unlock()
}
// addPeer takes a new peer as a pb.ServerMessage, optionally checks to see
// if they're online, and adds them to our list of peer. If we're not currently
// subscribed to a peer, it will also subscribe to it.
func (s *Server) addPeer(msg *pb.ServerMessage, ping bool) error {
if s.Args.Port == msg.Port &&
(localHosts[msg.Address] || msg.Address == s.Args.Host) {
log.Printf("%s:%s addPeer: Self peer, skipping...\n", s.Args.Host, s.Args.Port)
return nil
}
k := peerKey(msg)
newServer := &FederatedServer{
Address: msg.Address,
Port: msg.Port,
Ts: time.Now(),
}
log.Printf("%s:%s adding peer %+v\n", s.Args.Host, s.Args.Port, msg)
if oldServer, loaded := s.PeerServersLoadOrStore(newServer); !loaded {
if ping {
_, err := s.helloPeer(newServer)
if err != nil {
s.PeerServersMut.Lock()
delete(s.PeerServers, k)
s.PeerServersMut.Unlock()
return err
}
}
s.incNumPeers()
metrics.PeersKnown.Inc()
s.writePeers()
s.notifyPeerSubs(newServer)
// Subscribe to all our peers for now
err := s.subscribeToPeer(newServer)
if err != nil {
return err
} else {
s.Subscribed = true
}
} else {
oldServer.Ts = time.Now()
}
return nil
}
// mergeFederatedServers is an internal convenience function to add a list of
// peers.
func (s *Server) mergeFederatedServers(servers []*pb.ServerMessage) {
for _, srvMsg := range servers {
err := s.addPeer(srvMsg, false)
// This shouldn't happen because we're not pinging them.
if err != nil {
log.Println(err)
}
}
}
// makeHelloMessage makes a message for this hub to call the Hello endpoint
// on another hub.
func (s *Server) makeHelloMessage() *pb.HelloMessage {
servers := make([]*pb.ServerMessage, 0, 10)
s.PeerServersMut.RLock()
for _, peer := range s.PeerServers {
servers = append(servers, &pb.ServerMessage{
Address: peer.Address,
Port: peer.Port,
})
}
s.PeerServersMut.RUnlock()
return &pb.HelloMessage{
Port: s.Args.Port,
Host: s.Args.Host,
Servers: servers,
}
}

427
server/federation_test.go Normal file
View file

@ -0,0 +1,427 @@
package server
import (
"bufio"
"context"
"fmt"
"log"
"os"
"strings"
"testing"
"github.com/lbryio/hub/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go"
dto "github.com/prometheus/client_model/go"
"google.golang.org/grpc"
)
// lineCountFile takes a fileName and counts the number of lines in it.
func lineCountFile(fileName string) int {
f, err := os.Open(fileName)
defer f.Close()
if err != nil {
log.Println(err)
return 0
}
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanLines)
var lineCount = 0
for scanner.Scan() {
scanner.Text()
lineCount = lineCount + 1
}
return lineCount
}
// removeFile removes a file.
func removeFile(fileName string) {
err := os.Remove(fileName)
if err != nil {
log.Println(err)
}
}
func makeDefaultArgs() *Args {
args := &Args{
CmdType: ServeCmd,
Host: DefaultHost,
Port: DefaultPort,
EsHost: DefaultEsHost,
EsPort: DefaultEsPort,
UDPPort: DefaultUdpPort,
PrometheusPort: DefaultPrometheusPort,
EsIndex: DefaultEsIndex,
RefreshDelta: DefaultRefreshDelta,
CacheTTL: DefaultCacheTTL,
PeerFile: DefaultPeerFile,
Country: DefaultCountry,
DisableEs: true,
Debug: true,
LoadPeers: false,
StartPrometheus: false,
StartUDP: false,
WritePeers: false,
}
return args
}
// TestAddPeer tests the ability to add peers
func TestAddPeer(t *testing.T) {
ctx := context.Background()
args := makeDefaultArgs()
tests := []struct {
name string
want int
} {
{
name: "Add 10 peers",
want: 10,
},
{
name: "Add 10 peers, 1 unique",
want: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){
server := MakeHubServer(ctx, args)
server.Subscribed = true
metrics.PeersKnown.Set(0)
for i := 0; i < 10; i++ {
var msg *pb.ServerMessage
if strings.Contains(tt.name, "1 unique") {
msg = &pb.ServerMessage{
Address: "1.1.1.1",
Port: "50051",
}
} else {
x := i + 1
msg = &pb.ServerMessage{
Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x),
Port: "50051",
}
}
//log.Printf("Adding peer %+v\n", msg)
err := server.addPeer(msg, false)
if err != nil {
log.Println(err)
}
}
var m = &dto.Metric{}
if err := metrics.PeersKnown.Write(m); err != nil {
t.Errorf("Error getting metrics %+v\n", err)
}
got := int(*m.Gauge.Value)
if got != tt.want {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got, tt.want)
}
})
}
}
// TestPeerWriter tests that peers get written properly
func TestPeerWriter(t *testing.T) {
ctx := context.Background()
args := makeDefaultArgs()
args.WritePeers = true
tests := []struct {
name string
want int
} {
{
name: "Add 10 peers",
want: 10,
},
{
name: "Add 10 peers, 1 unique",
want: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){
server := MakeHubServer(ctx, args)
server.Subscribed = true
for i := 0; i < 10; i++ {
var msg *pb.ServerMessage
if strings.Contains(tt.name, "1 unique") {
msg = &pb.ServerMessage{
Address: "1.1.1.1",
Port: "50051",
}
} else {
x := i + 1
msg = &pb.ServerMessage{
Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x),
Port: "50051",
}
}
//log.Printf("Adding peer %+v\n", msg)
err := server.addPeer(msg, false)
if err != nil {
log.Println(err)
}
}
//log.Println("Counting lines...")
got := lineCountFile(server.Args.PeerFile)
if got != tt.want {
t.Errorf("lineCountFile(peers.txt) = %d, want %d", got, tt.want)
}
})
}
removeFile(args.PeerFile)
}
// TestAddPeerEndpoint tests the ability to add peers
func TestAddPeerEndpoint(t *testing.T) {
ctx := context.Background()
args := makeDefaultArgs()
args2 := makeDefaultArgs()
args2.Port = "50052"
tests := []struct {
name string
wantServerOne int64
wantServerTwo int64
} {
{
// outside -> server1.AddPeer(server2, ping=true) : server1 = 1, server2 = 0
// server1 -> server2.Hello(server1) : server1 = 1, server2 = 0
// server2 -> server2.addPeer(server1, ping=false) : server1 = 1, server2 = 1
// server2 -> server1.PeerSubscribe(server2) : server1 = 1, server2 = 1
// server1 <- server2.makeHelloMessage() : server1 = 1, server2 = 1
// server1.notifyPeer() : server1 = 1, server2 = 1
// server1 -> server2.AddPeer(server2) : server1 = 1, server2 = 1
// server2 self peer, skipping : server1 = 1, server2 = 1
// server1 -> server2.PeerSubscribe(server1) : server1 = 1, server2 = 1
name: "Add 1 peer",
wantServerOne: 1,
wantServerTwo: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){
server := MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2)
metrics.PeersKnown.Set(0)
go server.Run()
go server2.Run()
//go server.Run()
conn, err := grpc.Dial("localhost:"+args.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
c := pb.NewHubClient(conn)
msg := &pb.ServerMessage{
Address: "0.0.0.0",
Port: "50052",
}
_, err = c.AddPeer(context.Background(), msg)
if err != nil {
log.Println(err)
}
server.GrpcServer.GracefulStop()
server2.GrpcServer.GracefulStop()
got1 := server.getNumPeers()
got2 := server2.getNumPeers()
if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
}
if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
}
})
}
}
// TestAddPeerEndpoint2 tests the ability to add peers
func TestAddPeerEndpoint2(t *testing.T) {
ctx := context.Background()
args := makeDefaultArgs()
args2 := makeDefaultArgs()
args3 := makeDefaultArgs()
args2.Port = "50052"
args3.Port = "50053"
tests := []struct {
name string
wantServerOne int64
wantServerTwo int64
wantServerThree int64
} {
{
name: "Add 2 peers",
wantServerOne: 2,
wantServerTwo: 2,
wantServerThree: 2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){
server := MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2)
server3 := MakeHubServer(ctx, args3)
metrics.PeersKnown.Set(0)
go server.Run()
go server2.Run()
go server3.Run()
conn, err := grpc.Dial("localhost:"+args.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
c := pb.NewHubClient(conn)
msg := &pb.ServerMessage{
Address: "0.0.0.0",
Port: "50052",
}
msg2 := &pb.ServerMessage{
Address: "0.0.0.0",
Port: "50053",
}
_, err = c.AddPeer(context.Background(), msg)
if err != nil {
log.Println(err)
}
_, err = c.AddPeer(context.Background(), msg2)
if err != nil {
log.Println(err)
}
server.GrpcServer.GracefulStop()
server2.GrpcServer.GracefulStop()
server3.GrpcServer.GracefulStop()
got1 := server.getNumPeers()
got2 := server2.getNumPeers()
got3 := server3.getNumPeers()
if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
}
if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
}
if got3 != tt.wantServerThree {
t.Errorf("len(server3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree)
}
})
}
}
// TestAddPeerEndpoint3 tests the ability to add peers
func TestAddPeerEndpoint3(t *testing.T) {
ctx := context.Background()
args := makeDefaultArgs()
args2 := makeDefaultArgs()
args3 := makeDefaultArgs()
args2.Port = "50052"
args3.Port = "50053"
tests := []struct {
name string
wantServerOne int64
wantServerTwo int64
wantServerThree int64
} {
{
name: "Add 1 peer to each",
wantServerOne: 2,
wantServerTwo: 2,
wantServerThree: 2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){
server := MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2)
server3 := MakeHubServer(ctx, args3)
metrics.PeersKnown.Set(0)
go server.Run()
go server2.Run()
go server3.Run()
conn, err := grpc.Dial("localhost:"+args.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
conn2, err := grpc.Dial("localhost:50052",
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
c := pb.NewHubClient(conn)
c2 := pb.NewHubClient(conn2)
msg := &pb.ServerMessage{
Address: "0.0.0.0",
Port: "50052",
}
msg2 := &pb.ServerMessage{
Address: "0.0.0.0",
Port: "50053",
}
_, err = c.AddPeer(context.Background(), msg)
if err != nil {
log.Println(err)
}
_, err = c2.AddPeer(context.Background(), msg2)
if err != nil {
log.Println(err)
}
server.GrpcServer.GracefulStop()
server2.GrpcServer.GracefulStop()
server3.GrpcServer.GracefulStop()
got1 := server.getNumPeers()
got2 := server2.getNumPeers()
got3 := server3.getNumPeers()
if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
}
if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
}
if got3 != tt.wantServerThree {
t.Errorf("len(server3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree)
}
})
}
}

View file

@ -11,8 +11,6 @@ import (
"strings"
"time"
//"github.com/lbryio/hub/schema"
"github.com/lbryio/hub/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go"
"github.com/lbryio/lbry.go/v2/extras/util"
@ -24,8 +22,11 @@ import (
"gopkg.in/karalabe/cookiejar.v1/collections/deque"
)
// DefaultSearchSize is the default max number of items an
// es search will return.
const DefaultSearchSize = 1000
// record is a struct for the response from es.
type record struct {
Txid string `json:"tx_id"`
Nout uint32 `json:"tx_nout"`
@ -50,11 +51,14 @@ type record struct {
ClaimName string `json:"claim_name"`
}
// orderField is struct for specifying ordering of es search results.
type orderField struct {
Field string
IsAsc bool
}
// StrArrToInterface takes an array of strings and returns them as an array of
// interfaces.
func StrArrToInterface(arr []string) []interface{} {
searchVals := make([]interface{}, len(arr))
for i := 0; i < len(arr); i++ {
@ -63,6 +67,9 @@ func StrArrToInterface(arr []string) []interface{} {
return searchVals
}
// AddTermsField takes an es bool query, array of string values and a term
// name and adds a TermsQuery for that name matching those values to the
// bool query.
func AddTermsField(q *elastic.BoolQuery, arr []string, name string) *elastic.BoolQuery {
if len(arr) == 0 {
return q
@ -71,6 +78,9 @@ func AddTermsField(q *elastic.BoolQuery, arr []string, name string) *elastic.Boo
return q.Must(elastic.NewTermsQuery(name, searchVals...))
}
// AddTermField takes an es bool query, a string value and a term name
// and adds a TermQuery for that name matching that value to the bool
// query.
func AddTermField(q *elastic.BoolQuery, value string, name string) *elastic.BoolQuery {
if value != "" {
return q.Must(elastic.NewTermQuery(name, value))
@ -78,6 +88,9 @@ func AddTermField(q *elastic.BoolQuery, value string, name string) *elastic.Bool
return q
}
// AddIndividualTermFields takes a bool query, an array of string values
// a term name, and a bool to invert the query, and adds multiple individual
// TermQuerys for that name matching each of the values.
func AddIndividualTermFields(q *elastic.BoolQuery, arr []string, name string, invert bool) *elastic.BoolQuery {
for _, x := range arr {
if invert {
@ -89,6 +102,8 @@ func AddIndividualTermFields(q *elastic.BoolQuery, arr []string, name string, in
return q
}
// AddRangeField takes a bool query, a range field struct and a term name
// and adds a term query for that name matching that range field.
func AddRangeField(q *elastic.BoolQuery, rq *pb.RangeField, name string) *elastic.BoolQuery {
if rq == nil {
return q
@ -112,6 +127,8 @@ func AddRangeField(q *elastic.BoolQuery, rq *pb.RangeField, name string) *elasti
}
}
// AddInvertibleField takes a bool query, an invertible field and a term name
// and adds a term query for that name matching that invertible field.
func AddInvertibleField(q *elastic.BoolQuery, field *pb.InvertibleField, name string) *elastic.BoolQuery {
if field == nil {
return q
@ -128,11 +145,16 @@ func AddInvertibleField(q *elastic.BoolQuery, field *pb.InvertibleField, name st
}
}
// recordErrorAndDie is for fatal errors. It takes an error, increments the
// fatal error metric in prometheus and prints a fatal error message.
func (s *Server) recordErrorAndDie(err error) {
metrics.ErrorsCounter.With(prometheus.Labels{"error_type": "fatal"}).Inc()
log.Fatalln(err)
}
// RoundUpReleaseTime take a bool query, a range query and a term name
// and adds a term query for that name (this is for the release time
// field) with the value rounded up.
func RoundUpReleaseTime(q *elastic.BoolQuery, rq *pb.RangeField, name string) *elastic.BoolQuery {
if rq == nil {
return q
@ -170,6 +192,11 @@ func RoundUpReleaseTime(q *elastic.BoolQuery, rq *pb.RangeField, name string) *e
// 8) return streams referenced by repost and all channel referenced in extra_txos
//*/
func (s *Server) Search(ctx context.Context, in *pb.SearchRequest) (*pb.Outputs, error) {
if s.Args.DisableEs {
log.Println("ElasticSearch disable, return nil to search")
return nil, nil
}
metrics.RequestsCount.With(prometheus.Labels{"method": "search"}).Inc()
defer func(t time.Time) {
delta := time.Since(t).Seconds()
@ -301,6 +328,7 @@ func (s *Server) Search(ctx context.Context, in *pb.SearchRequest) (*pb.Outputs,
}, nil
}
// normalizeTag takes a string and normalizes it for search in es.
func (s *Server) normalizeTag(tag string) string {
c := cases.Lower(language.English)
res := s.MultiSpaceRe.ReplaceAll(
@ -312,6 +340,7 @@ func (s *Server) normalizeTag(tag string) string {
return string(res)
}
// cleanTags takes an array of tags and normalizes them.
func (s *Server) cleanTags(tags []string) []string {
cleanedTags := make([]string, len(tags))
for i, tag := range tags {
@ -320,6 +349,8 @@ func (s *Server) cleanTags(tags []string) []string {
return cleanedTags
}
// searchResultToRecords takes an elastic.SearchResult object and converts
// them to internal record structures.
func (s *Server) searchResultToRecords(
searchResult *elastic.SearchResult) []*record {
records := make([]*record, 0, searchResult.TotalHits())
@ -334,6 +365,9 @@ func (s *Server) searchResultToRecords(
return records
}
// postProcessResults takes es search result records and runs our
// post processing on them.
// TODO: more in depth description.
func (s *Server) postProcessResults(
ctx context.Context,
client *elastic.Client,
@ -397,6 +431,8 @@ func (s *Server) postProcessResults(
return txos, extraTxos, blocked
}
// checkQuery takes a search request and does a sanity check on it for
// validity.
func (s *Server) checkQuery(in *pb.SearchRequest) error {
limit := 2048
checks := map[string]bool{
@ -418,6 +454,8 @@ func (s *Server) checkQuery(in *pb.SearchRequest) error {
return nil
}
// setPageVars takes a search request and pointers to the local pageSize
// and from variables and sets them from the struct.
func setPageVars(in *pb.SearchRequest, pageSize *int, from *int) {
if in.Limit > 0 {
log.Printf("############ limit: %d\n", in.Limit)
@ -429,6 +467,8 @@ func setPageVars(in *pb.SearchRequest, pageSize *int, from *int) {
}
}
// setupEsQuery takes an elastic.BoolQuery, pb.SearchRequest and orderField
// and adds the search request terms to the bool query.
func (s *Server) setupEsQuery(
q *elastic.BoolQuery,
in *pb.SearchRequest,
@ -625,6 +665,8 @@ func (s *Server) setupEsQuery(
return q
}
// getUniqueChannels takes the record results from the es search and returns
// the unique channels from those records as a list and a map.
func (s *Server) getUniqueChannels(records []*record, client *elastic.Client, ctx context.Context, searchIndices []string) ([]*pb.Output, map[string]*pb.Output) {
channels := make(map[string]*pb.Output)
channelsSet := make(map[string]bool)
@ -678,6 +720,9 @@ func (s *Server) getUniqueChannels(records []*record, client *elastic.Client, ct
return channelTxos, channels
}
// getClaimsForReposts takes the record results from the es query and returns
// an array and map of the reposted records as well as an array of those
// records.
func (s *Server) getClaimsForReposts(ctx context.Context, client *elastic.Client, records []*record, searchIndices []string) ([]*pb.Output, []*record, map[string]*pb.Output) {
var totalReposted = 0
@ -731,10 +776,8 @@ func (s *Server) getClaimsForReposts(ctx context.Context, client *elastic.Client
return claims, repostedRecords, respostedMap
}
/*
Takes a search request and serializes into a string for use as a key into the
internal cache for the hub.
*/
// serializeSearchRequest takes a search request and serializes it into a key
// for use in the internal cache for the hub.
func (s *Server) serializeSearchRequest(request *pb.SearchRequest) string {
// Save the offest / limit and set to zero, cache hits should happen regardless
// and they're used in post processing
@ -754,6 +797,8 @@ func (s *Server) serializeSearchRequest(request *pb.SearchRequest) string {
return str
}
// searchAhead takes an array of record results, the pageSize and
// perChannelPerPage value and returns the hits for this page.
func searchAhead(searchHits []*record, pageSize int, perChannelPerPage int) []*record {
finalHits := make([]*record, 0, len(searchHits))
var channelCounters map[string]int
@ -796,6 +841,8 @@ func searchAhead(searchHits []*record, pageSize int, perChannelPerPage int) []*r
return finalHits
}
// recordToOutput is a function on a record struct to turn it into a pb.Output
// struct.
func (r *record) recordToOutput() *pb.Output {
return &pb.Output{
TxHash: util.TxIdToTxHash(r.Txid),
@ -822,6 +869,8 @@ func (r *record) recordToOutput() *pb.Output {
}
}
// getHitId is a function on the record struct to get the id for the search
// hit.
func (r *record) getHitId() string {
if r.RepostedClaimId != "" {
return r.RepostedClaimId
@ -830,6 +879,7 @@ func (r *record) getHitId() string {
}
}
// removeDuplicates takes an array of record results and remove duplicates.
func removeDuplicates(searchHits []*record) []*record {
dropped := make(map[*record]bool)
// claim_id -> (creation_height, hit_id), where hit_id is either reposted claim id or original
@ -865,6 +915,8 @@ func removeDuplicates(searchHits []*record) []*record {
return deduped
}
// removeBlocked takes an array of record results from the es search
// and removes blocked records.
func removeBlocked(searchHits []*record) ([]*record, []*record, map[string]*pb.Blocked) {
newHits := make([]*record, 0, len(searchHits))
blockedHits := make([]*record, 0, len(searchHits))

View file

@ -6,9 +6,11 @@ import (
"fmt"
"hash"
"log"
"net"
"net/http"
"os"
"regexp"
"sync"
"time"
"github.com/ReneKroon/ttlcache/v2"
@ -19,47 +21,30 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type Server struct {
GrpcServer *grpc.Server
Args *Args
MultiSpaceRe *regexp.Regexp
WeirdCharsRe *regexp.Regexp
EsClient *elastic.Client
Servers []*FederatedServer
QueryCache *ttlcache.Cache
S256 *hash.Hash
GrpcServer *grpc.Server
Args *Args
MultiSpaceRe *regexp.Regexp
WeirdCharsRe *regexp.Regexp
EsClient *elastic.Client
QueryCache *ttlcache.Cache
S256 *hash.Hash
LastRefreshCheck time.Time
RefreshDelta time.Duration
NumESRefreshes int64
PeerServers map[string]*FederatedServer
PeerServersMut sync.RWMutex
NumPeerServers *int64
PeerSubs map[string]*FederatedServer
PeerSubsMut sync.RWMutex
NumPeerSubs *int64
Subscribed bool
pb.UnimplementedHubServer
}
type FederatedServer struct {
Address string
Port string
Ts time.Time
Ping int //?
}
const (
ServeCmd = iota
SearchCmd = iota
)
type Args struct {
// TODO Make command types an enum
CmdType int
Host string
Port string
EsHost string
EsPort string
EsIndex string
Debug bool
RefreshDelta int
CacheTTL int
}
func getVersion() string {
return meta.Version
@ -104,7 +89,56 @@ func getVersion() string {
'blockchain.address.unsubscribe'
*/
func MakeHubServer(args *Args) *Server {
func (s *Server) PeerSubsLoadOrStore(peer *FederatedServer) (actual *FederatedServer, loaded bool) {
key := peer.peerKey()
s.PeerSubsMut.RLock()
if actual, ok := s.PeerSubs[key]; ok {
s.PeerSubsMut.RUnlock()
return actual, true
} else {
s.PeerSubsMut.RUnlock()
s.PeerSubsMut.Lock()
s.PeerSubs[key] = peer
s.PeerSubsMut.Unlock()
return peer, false
}
}
func (s *Server) PeerServersLoadOrStore(peer *FederatedServer) (actual *FederatedServer, loaded bool) {
key := peer.peerKey()
s.PeerServersMut.RLock()
if actual, ok := s.PeerServers[key]; ok {
s.PeerServersMut.RUnlock()
return actual, true
} else {
s.PeerServersMut.RUnlock()
s.PeerServersMut.Lock()
s.PeerServers[key] = peer
s.PeerServersMut.Unlock()
return peer, false
}
}
func (s *Server) Run() {
l, err := net.Listen("tcp", ":"+s.Args.Port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
pb.RegisterHubServer(s.GrpcServer, s)
reflection.Register(s.GrpcServer)
log.Printf("listening on %s\n", l.Addr().String())
log.Println(s.Args)
if err := s.GrpcServer.Serve(l); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
// MakeHubServer takes the arguments given to a hub when it's started and
// initializes everything. It loads information about previously known peers,
// creates needed internal data structures, and initializes goroutines.
func MakeHubServer(ctx context.Context, args *Args) *Server {
grpcServer := grpc.NewServer(grpc.NumStreamWorkers(10))
multiSpaceRe, err := regexp.Compile(`\s{2,}`)
@ -116,23 +150,23 @@ func MakeHubServer(args *Args) *Server {
if err != nil {
log.Fatal(err)
}
self := &FederatedServer{
Address: "127.0.0.1",
Port: args.Port,
Ts: time.Now(),
Ping: 0,
}
servers := make([]*FederatedServer, 10)
servers = append(servers, self)
esUrl := args.EsHost + ":" + args.EsPort
opts := []elastic.ClientOptionFunc{elastic.SetSniff(false), elastic.SetURL(esUrl)}
if args.Debug {
opts = append(opts, elastic.SetTraceLog(log.New(os.Stderr, "[[ELASTIC]]", 0)))
}
client, err := elastic.NewClient(opts...)
if err != nil {
log.Fatal(err)
var client *elastic.Client = nil
if !args.DisableEs {
esUrl := args.EsHost + ":" + args.EsPort
opts := []elastic.ClientOptionFunc{
elastic.SetSniff(true),
elastic.SetSnifferTimeoutStartup(time.Second * 60),
elastic.SetSnifferTimeout(time.Second * 60),
elastic.SetURL(esUrl),
}
if args.Debug {
opts = append(opts, elastic.SetTraceLog(log.New(os.Stderr, "[[ELASTIC]]", 0)))
}
client, err = elastic.NewClient(opts...)
if err != nil {
log.Fatal(err)
}
}
cache := ttlcache.NewCache()
@ -146,45 +180,130 @@ func MakeHubServer(args *Args) *Server {
refreshDelta = time.Second * 0
}
numPeers := new(int64)
*numPeers = 0
numSubs := new(int64)
*numSubs = 0
s := &Server{
GrpcServer: grpcServer,
Args: args,
MultiSpaceRe: multiSpaceRe,
WeirdCharsRe: weirdCharsRe,
EsClient: client,
QueryCache: cache,
S256: &s256,
Args: args,
MultiSpaceRe: multiSpaceRe,
WeirdCharsRe: weirdCharsRe,
EsClient: client,
QueryCache: cache,
S256: &s256,
LastRefreshCheck: time.Now(),
RefreshDelta: refreshDelta,
RefreshDelta: refreshDelta,
NumESRefreshes: 0,
PeerServers: make(map[string]*FederatedServer),
PeerServersMut: sync.RWMutex{},
NumPeerServers: numPeers,
PeerSubs: make(map[string]*FederatedServer),
PeerSubsMut: sync.RWMutex{},
NumPeerSubs: numSubs,
Subscribed: false,
}
// Start up our background services
if args.StartPrometheus {
go s.prometheusEndpoint(s.Args.PrometheusPort, "metrics")
}
if args.StartUDP {
go func() {
err := UDPServer(args)
if err != nil {
log.Println("UDP Server failed!", err)
}
}()
}
// Load peers from disk and subscribe to one if there are any
if args.LoadPeers {
err = s.loadPeers()
if err != nil {
log.Println(err)
}
}
return s
}
func (s *Server) PromethusEndpoint(port string, endpoint string) error {
// prometheusEndpoint is a goroutine which start up a prometheus endpoint
// for this hub to allow for metric tracking.
func (s *Server) prometheusEndpoint(port string, endpoint string) {
http.Handle("/"+endpoint, promhttp.Handler())
log.Println(fmt.Sprintf("listening on :%s /%s", port, endpoint))
err := http.ListenAndServe(":"+port, nil)
if err != nil {
return err
log.Fatalln("Shouldn't happen??!?!", err)
}
// Hello is a grpc endpoint to allow another hub to tell us about itself.
// The passed message includes information about the other hub, and all
// of its peers which are added to the knowledge of this hub.
func (s *Server) Hello(ctx context.Context, args *pb.HelloMessage) (*pb.HelloMessage, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "hello"}).Inc()
port := args.Port
host := args.Host
server := &FederatedServer{
Address: host,
Port: port,
Ts: time.Now(),
}
log.Fatalln("Shouldn't happen??!?!")
return nil
log.Println(server)
err := s.addPeer(&pb.ServerMessage{Address: host, Port: port}, false)
// They just contacted us, so this shouldn't happen
if err != nil {
log.Println(err)
}
s.mergeFederatedServers(args.Servers)
s.writePeers()
s.notifyPeerSubs(server)
return s.makeHelloMessage(), nil
}
func (s *Server) Hello(context context.Context, args *FederatedServer) (*FederatedServer, error) {
s.Servers = append(s.Servers, args)
// PeerSubscribe adds a peer hub to the list of subscribers to update about
// new peers.
func (s *Server) PeerSubscribe(ctx context.Context, in *pb.ServerMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "peer_subscribe"}).Inc()
var msg = "Success"
peer := &FederatedServer{
Address: in.Address,
Port: in.Port,
Ts: time.Now(),
}
return s.Servers[0], nil
if _, loaded := s.PeerSubsLoadOrStore(peer); !loaded {
s.incNumSubs()
metrics.PeersSubscribed.Inc()
} else {
msg = "Already subscribed"
}
return &pb.StringValue{Value: msg}, nil
}
func (s *Server) Ping(context context.Context, args *pb.EmptyMessage) (*pb.StringValue, error) {
// AddPeer is a grpc endpoint to tell this hub about another hub in the network.
func (s *Server) AddPeer(ctx context.Context, args *pb.ServerMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "add_peer"}).Inc()
var msg = "Success"
err := s.addPeer(args, true)
if err != nil {
log.Println(err)
msg = "Failed"
}
return &pb.StringValue{Value: msg}, err
}
// Ping is a grpc endpoint that returns a short message.
func (s *Server) Ping(ctx context.Context, args *pb.EmptyMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "ping"}).Inc()
return &pb.StringValue{Value: "Hello, world!"}, nil
}
func (s *Server) Version(context context.Context, args *pb.EmptyMessage) (*pb.StringValue, error) {
// Version is a grpc endpoint to get this hub's version.
func (s *Server) Version(ctx context.Context, args *pb.EmptyMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "version"}).Inc()
return &pb.StringValue{Value: getVersion()}, nil
}

226
server/udp.go Normal file
View file

@ -0,0 +1,226 @@
package server
import (
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/lbryio/lbry.go/v2/extras/errors"
)
const maxBufferSize = 1024
// genesis blocktime (which is actually wrong)
const magic = 1446058291
const protocolVersion = 1
// SPVPing is a struct for the format of how to ping another hub over udp.
// format b'!lB64s'
type SPVPing struct {
magic uint32
version byte
padding []byte //64
}
// SPVPong is a struct for the return pong from another hub server.
// format b'!BBL32s4sH'
type SPVPong struct {
protocolVersion byte
flags byte
height uint32
tip []byte // 32
srcAddrRaw []byte // 4
country uint16
}
// encodeSPVPing creates a slice of bytes to ping another hub with
// over udp.
func encodeSPVPing() []byte {
data := make([]byte, 69)
binary.BigEndian.PutUint32(data, magic)
data[4] = protocolVersion
return data
}
// decodeSPVPing takes a slice of bytes and decodes an SPVPing struct from them.
func decodeSPVPing(data []byte) *SPVPing {
if len(data) < 69 {
return nil
}
parsedMagic := binary.BigEndian.Uint32(data)
parsedProtocalVersion := data[4]
return &SPVPing{
magic: parsedMagic,
version: parsedProtocalVersion,
}
}
// Encode is a function for SPVPong structs to encode them into bytes for
// sending over udp.
func (pong *SPVPong) Encode() []byte {
data := make([]byte, 44)
data[0] = pong.protocolVersion
data[1] = pong.flags
binary.BigEndian.PutUint32(data[2:], pong.height)
copy(data[6:], pong.tip)
copy(data[38:], pong.srcAddrRaw)
binary.BigEndian.PutUint16(data[42:], pong.country)
return data
}
// makeSPVPong creates an SPVPong struct according to given parameters.
// FIXME: Currently, does not correctly encode the country.
func makeSPVPong(flags int, height int, tip []byte, sourceAddr string, country string) *SPVPong {
byteAddr := EncodeAddress(sourceAddr)
countryInt := 1
return &SPVPong{
protocolVersion: protocolVersion,
flags: byte(flags),
height: uint32(height),
tip: tip,
srcAddrRaw: byteAddr,
country: uint16(countryInt),
}
}
// decodeSPVPong takes a slice of bytes and decodes an SPVPong struct
// from it.
func decodeSPVPong(data []byte) *SPVPong {
if len(data) < 44 {
return nil
}
parsedProtocalVersion := data[0]
flags := data[1]
height := binary.BigEndian.Uint32(data[:2])
tip := make([]byte, 32)
copy(tip, data[6:38])
srcRawAddr := make([]byte, 4)
copy(srcRawAddr, data[38:42])
country := binary.BigEndian.Uint16(data[:42])
return &SPVPong{
protocolVersion: parsedProtocalVersion,
flags: flags,
height: height,
tip: tip,
srcAddrRaw: srcRawAddr,
country: country,
}
}
// EncodeAddress takes an ipv4 address and encodes it into bytes for the hub
// Ping/Pong protocol.
func EncodeAddress(addr string) []byte {
parts := strings.Split(addr, ".")
if len(parts) != 4 {
return []byte{}
}
data := make([]byte, 4)
for i, part := range parts {
x, err := strconv.Atoi(part)
if err != nil || x > 255 {
return []byte{}
}
data[i] = byte(x)
}
return data
}
// DecodeAddress gets the string ipv4 address from an SPVPong struct.
func (pong *SPVPong) DecodeAddress() string {
return fmt.Sprintf("%d.%d.%d.%d",
pong.srcAddrRaw[0],
pong.srcAddrRaw[1],
pong.srcAddrRaw[2],
pong.srcAddrRaw[3],
)
}
// UDPPing sends a ping over udp to another hub and returns the ip address of
// this hub.
func UDPPing(address string) (string, error) {
addr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
return "", err
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return "", err
}
defer conn.Close()
_, err = conn.Write(encodeSPVPing())
if err != nil {
return "", err
}
buffer := make([]byte, maxBufferSize)
deadline := time.Now().Add(time.Second)
err = conn.SetReadDeadline(deadline)
if err != nil {
return "", err
}
n, _, err := conn.ReadFromUDP(buffer)
if err != nil {
return "", err
}
pong := decodeSPVPong(buffer[:n])
if pong == nil {
return "", errors.Base("Pong decoding failed")
}
myAddr := pong.DecodeAddress()
return myAddr, nil
}
// UDPServer is a goroutine that starts an udp server that implements the hubs
// Ping/Pong protocol to find out about each other without making full TCP
// connections.
func UDPServer(args *Args) error {
address := ":" + args.UDPPort
tip := make([]byte, 32)
addr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
return err
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return err
}
defer conn.Close()
buffer := make([]byte, maxBufferSize)
for {
//TODO verify ping
_, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
return err
}
sAddr := addr.IP.String()
pong := makeSPVPong(0,0, tip, sAddr, args.Country)
data := pong.Encode()
_, err = conn.WriteToUDP(data, addr)
if err != nil {
return err
}
}
}