Most of federation is written, need to finish udp and test

Cleanup, more reorg, more arguments, started adding tests
Comments and another test
Simplify writing of peers and add unit test
This commit is contained in:
Jeffrey Picard 2021-10-24 21:39:37 -04:00
parent 45e9817ced
commit 5387aeeebe
13 changed files with 1871 additions and 466 deletions

2
go.mod
View file

@ -12,6 +12,6 @@ require (
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=

165
main.go
View file

@ -5,11 +5,8 @@ import (
"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"
@ -17,176 +14,36 @@ import (
"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)
l, err := net.Listen("tcp", ":"+args.Port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := server.MakeHubServer(args)
s := server.MakeHubServer(ctxWCancel, 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)
}
return
}
conn, err := grpc.Dial("localhost"+args.Port,
conn, err := grpc.Dial("localhost:"+args.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
@ -197,13 +54,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,10 @@ 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 PeerSubscribeStreaming (ServerMessage) returns (stream ServerMessage) {}
rpc PeerSubscribe (ServerMessage) returns (StringValue) {}
rpc Version (EmptyMessage) returns (StringValue) {}
rpc Features (EmptyMessage) returns (StringValue) {}
rpc Broadcast(EmptyMessage) returns (UInt32Value) {}
@ -15,6 +19,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,210 @@ 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,
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, 0xcc, 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, 0x42, 0x0a, 0x16, 0x50, 0x65,
0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x69, 0x6e, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 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, 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,
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 +1185,68 @@ 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.PeerSubscribeStreaming:input_type -> pb.ServerMessage
2, // 29: pb.Hub.PeerSubscribe:input_type -> pb.ServerMessage
1, // 30: pb.Hub.Version:input_type -> pb.EmptyMessage
1, // 31: pb.Hub.Features:input_type -> pb.EmptyMessage
1, // 32: pb.Hub.Broadcast:input_type -> pb.EmptyMessage
10, // 33: pb.Hub.Search:output_type -> pb.Outputs
5, // 34: pb.Hub.Ping:output_type -> pb.StringValue
3, // 35: pb.Hub.Hello:output_type -> pb.HelloMessage
5, // 36: pb.Hub.AddPeer:output_type -> pb.StringValue
2, // 37: pb.Hub.PeerSubscribeStreaming:output_type -> pb.ServerMessage
5, // 38: pb.Hub.PeerSubscribe:output_type -> pb.StringValue
5, // 39: pb.Hub.Version:output_type -> pb.StringValue
5, // 40: pb.Hub.Features:output_type -> pb.StringValue
7, // 41: pb.Hub.Broadcast:output_type -> pb.UInt32Value
33, // [33:42] is the sub-list for method output_type
24, // [24:33] 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 +1269,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 +1281,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 +1293,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 +1305,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 +1317,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 +1329,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 +1371,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,10 @@ 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)
PeerSubscribeStreaming(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (Hub_PeerSubscribeStreamingClient, 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 +55,65 @@ 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) PeerSubscribeStreaming(ctx context.Context, in *ServerMessage, opts ...grpc.CallOption) (Hub_PeerSubscribeStreamingClient, error) {
stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[0], "/pb.Hub/PeerSubscribeStreaming", opts...)
if err != nil {
return nil, err
}
x := &hubPeerSubscribeStreamingClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Hub_PeerSubscribeStreamingClient interface {
Recv() (*ServerMessage, error)
grpc.ClientStream
}
type hubPeerSubscribeStreamingClient struct {
grpc.ClientStream
}
func (x *hubPeerSubscribeStreamingClient) Recv() (*ServerMessage, error) {
m := new(ServerMessage)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, 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 +147,10 @@ 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)
PeerSubscribeStreaming(*ServerMessage, Hub_PeerSubscribeStreamingServer) 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 +167,18 @@ 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) PeerSubscribeStreaming(*ServerMessage, Hub_PeerSubscribeStreamingServer) error {
return status.Errorf(codes.Unimplemented, "method PeerSubscribeStreaming 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 +237,81 @@ 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_PeerSubscribeStreaming_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ServerMessage)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(HubServer).PeerSubscribeStreaming(m, &hubPeerSubscribeStreamingServer{stream})
}
type Hub_PeerSubscribeStreamingServer interface {
Send(*ServerMessage) error
grpc.ServerStream
}
type hubPeerSubscribeStreamingServer struct {
grpc.ServerStream
}
func (x *hubPeerSubscribeStreamingServer) Send(m *ServerMessage) error {
return x.ServerStream.SendMsg(m)
}
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 +381,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,
@ -240,6 +406,12 @@ var Hub_ServiceDesc = grpc.ServiceDesc{
Handler: _Hub_Broadcast_Handler,
},
},
Streams: []grpc.StreamDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "PeerSubscribeStreaming",
Handler: _Hub_PeerSubscribeStreaming_Handler,
ServerStreams: true,
},
},
Metadata: "hub.proto",
}

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:')

200
server/args.go Normal file
View file

@ -0,0 +1,200 @@
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
DisableEs bool
PrometheusPort string
EsIndex string
Debug bool
RefreshDelta int
CacheTTL int
PeerFile string
Country string
StartPeerAdder 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"
DefaultStartPeerAdder = 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")
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})
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})
startPeerAdder := parser.Flag("", "start-peer-adder", &argparse.Options{Required: false, Help: "Start peer adder service", Default: DefaultStartPeerAdder})
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,
DisableEs: *disableEs,
PrometheusPort: *prometheusPort,
EsIndex: *esIndex,
Debug: *debug,
RefreshDelta: *refreshDelta,
CacheTTL: *cacheTTL,
PeerFile: *peerFile,
Country: *country,
StartPeerAdder: *startPeerAdder,
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
}

422
server/federation.go Normal file
View file

@ -0,0 +1,422 @@
package server
import (
"bufio"
"context"
"log"
"math"
"os"
"strings"
"time"
pb "github.com/lbryio/hub/protobuf/go"
"google.golang.org/grpc"
)
// sub is an internal structure holding information about an open connection
// with a peer.
type sub struct {
stream pb.Hub_PeerSubscribeStreamingServer
done chan<- bool
}
// peerAddMsg is an internal structure for use in the channel communicating
// to the peerAdder gorountine.
type peerAddMsg struct {
msg *pb.ServerMessage
ping bool
}
// FederatedServer hold relevant information about peers that we known about.
type FederatedServer struct {
Address string
Port string
Ts time.Time
}
// 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
}
// 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 loadPeers(args *Args) map[string]*FederatedServer {
localHosts := map[string]bool {
"127.0.0.1": true,
"0.0.0.0": true,
"localhost": true,
}
servers := make(map[string]*FederatedServer)
peerFile := args.PeerFile
port := args.Port
f, err := os.Open(peerFile)
if err != nil {
log.Println(err)
return map[string]*FederatedServer{}
}
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(args)
log.Println(ipPort)
if ipPort[1] == port && localHosts[ipPort[0]] {
log.Println("Self peer, skipping ...")
continue
}
server := &FederatedServer{
Address: ipPort[0],
Port: ipPort[1],
Ts: time.Now(),
}
log.Println("pinging peer", server)
if helloPeer(server, args) {
servers[line] = server
}
}
log.Println("Returning from loadPeers")
return servers
}
// 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
}
// 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 true is call was successful, false otherwise.
func helloPeer(server *FederatedServer, args *Args) bool {
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 false
}
defer conn.Close()
c := pb.NewHubClient(conn)
msg := &pb.HelloMessage{
Port: args.Port,
Host: args.Host,
Servers: []*pb.ServerMessage{},
}
res, err := c.Hello(ctx, msg)
if err != nil {
log.Println(err)
return false
}
log.Println(res)
return true
}
// writePeers writes our current known peers to disk
// FIXME: This is probably inefficient, we just truncate the file and write
// the entire thing every time. Maybe use some sort of mmap?
func (s *Server) writePeers() {
if !s.Args.WritePeers {
return
}
failedCreat := "WARNING: Peer writer failed to create peer file, it's still running but may not be working!"
failedWrite := "WARNING: Peer writer failed to write a line, it's still running but may not be working!"
failedFlush := "WARNING: Peer writer failed to flush, it's still running but may not be working!"
failedClose := "WARNING: Peer writer failed to close the peer file, it's still running but may not be working!"
f, err := os.Create(s.Args.PeerFile)
if err != nil {
log.Println(failedCreat)
log.Println(err)
}
writer := bufio.NewWriter(f)
for _, peer := range s.Servers {
line := peer.Address + ":" + peer.Port + "\n"
_, err := writer.WriteString(line)
if err != nil {
log.Println(failedWrite)
log.Println(err)
}
}
err = writer.Flush()
if err != nil {
log.Println(failedFlush)
log.Println(err)
}
err = f.Close()
if err != nil {
log.Println(failedClose)
log.Println(err)
}
}
// peerAdder is a goroutine which listens for new peers added and then
// optionally checks if they're online and adds them to our map of
// peers in a thread safe manner.
func (s *Server) peerAdder(ctx context.Context) {
for {
select {
case chanMsg := <-s.peerChannel:
msg := chanMsg.msg
ping := chanMsg.ping
k := msg.Address + ":" + msg.Port
if _, ok := s.Servers[k]; !ok {
newServer := &FederatedServer{
Address: msg.Address,
Port: msg.Port,
Ts: time.Now(),
}
log.Println(!ping)
if !ping || helloPeer(newServer, s.Args) {
s.Servers[k] = newServer
s.writePeers()
s.notifyPeerSubs(newServer)
}
} else {
s.Servers[k].Ts = time.Now()
}
case <-ctx.Done():
log.Println("context finished, peerAdder shutting down.")
return
}
}
}
// getFastestPeer determines the fastest peer in its list of peers by sending
// out udp pings and seeing who responds first. This is currently not
// implemented.
func (s *Server) getFastestPeer() *FederatedServer {
log.Println(s.Servers)
if len(s.Servers) == 0 {
return nil
}
for _, peer := range s.Servers {
return peer
}
return nil
}
// subscribeToPeer subscribes to a given peer hub in a streaming fashion.
func (s *Server) subscribeToPeer(peer *FederatedServer) {
var msg *pb.ServerMessage
log.Println("Subscribing to peer: ", peer)
if peer == nil {
return
}
conn, err := grpc.Dial(
peer.Address+":"+peer.Port,
grpc.WithInsecure(),
grpc.WithBlock(),
)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
c := pb.NewHubClient(conn)
var retries = 0
var stream pb.Hub_PeerSubscribeStreamingClient
for retries <= 3 {
if stream == nil {
stream, err = c.PeerSubscribeStreaming(
context.Background(),
&pb.ServerMessage{Address: s.Args.Host, Port: s.Args.Port},
)
}
if err != nil {
goto retry
}
err = stream.RecvMsg(msg)
if err != nil {
goto retry
}
s.addPeer(msg, false)
continue
retry:
retries = retries + 1
time.Sleep(time.Second * time.Duration(int(math.Pow(10, float64(retries)))))
}
}
// subscribeToFastestPeer is a convenience function to find and subscribe to
// the fastest peer we know about in a streaming fashion.
func (s *Server) subscribeToFastestPeer(keepSubscribed bool) {
for {
peer := s.getFastestPeer()
s.subscribeToPeer(peer)
if !keepSubscribed {
return
}
// Put in a sleep, so we aren't looping like crazy if we have no peers
time.Sleep(time.Second * 5)
}
}
// notifyPeerSubsStreaming notifies peer subs of new peers in a streaming
// fashion.
func (s *Server) notifyPeerSubsStreaming(newServer *FederatedServer) {
msg := &pb.ServerMessage{
Address: newServer.Address,
Port: newServer.Port,
}
var unsubscribe []string
s.PeerSubs.Range(func(k, v interface{}) bool {
key, ok := k.(string)
if !ok {
log.Println("Failed to cast subscriber key: ", v)
return true
}
peer, ok := v.(sub)
if !ok {
log.Println("Failed to cast subscriber value: ", v)
return true
}
log.Printf("Notifying peer %s of new node %+v\n", key, msg)
err := peer.stream.Send(msg)
if err != nil {
log.Println("Failed to send data to ", key)
select {
case peer.done <- true:
log.Println("Unsubscribed ", key)
default:
}
unsubscribe = append(unsubscribe, key)
}
return true
})
for _, key := range unsubscribe {
s.PeerSubs.Delete(key)
}
}
func (s *Server) notifyPeerSubs(newServer *FederatedServer) {
var unsubscribe []string
s.PeerSubs.Range(func(k, v interface{}) bool {
key, ok := k.(string)
if !ok {
log.Println("Failed to cast subscriber key: ", v)
return true
}
peer, ok := v.(*FederatedServer)
if !ok {
log.Println("Failed to cast subscriber value: ", v)
return true
}
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)
}
return true
})
for _, key := range unsubscribe {
s.PeerSubs.Delete(key)
}
}
// addPeer is an internal function to add a peer to this hub.
func (s *Server) addPeer(msg *pb.ServerMessage, ping bool) {
s.peerChannel <- &peerAddMsg{msg, ping}
}
// mergeFederatedServers is an internal convenience function to add a list of
// peers.
func (s *Server) mergeFederatedServers(servers []*pb.ServerMessage) {
for _, srvMsg := range servers {
s.peerChannel <- &peerAddMsg{srvMsg, false}
}
}
// makeHelloMessage makes a message for this hub to call the Hello endpoint
// on another hub.
func (s *Server) makeHelloMessage() *pb.HelloMessage {
n := len(s.Servers)
servers := make([]*pb.ServerMessage, n)
var i = 0
for _, v := range s.Servers {
servers[i] = &pb.ServerMessage{
Address: v.Address,
Port: v.Port,
}
i += 1
}
return &pb.HelloMessage{
Port: s.Args.Port,
Host: s.Args.Host,
Servers: servers,
}
}

199
server/federation_test.go Normal file
View file

@ -0,0 +1,199 @@
package server
import (
"bufio"
"context"
"fmt"
"log"
"os"
"strings"
"testing"
"time"
pb "github.com/lbryio/hub/protobuf/go"
)
// 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)
}
}
// TestPeerAdder tests the peer adder goroutine.
func TestPeerAdder(t *testing.T) {
ctx := context.Background()
args := &Args{
CmdType: ServeCmd,
Host: DefaultHost,
Port: DefaultPort,
EsHost: DefaultEsHost,
EsPort: DefaultEsPort,
UDPPort: DefaultUdpPort,
DisableEs: true,
PrometheusPort: DefaultPrometheusPort,
EsIndex: DefaultEsIndex,
Debug: true,
RefreshDelta: DefaultRefreshDelta,
CacheTTL: DefaultCacheTTL,
PeerFile: DefaultPeerFile,
Country: DefaultCountry,
StartPeerAdder: false,
StartPrometheus: false,
StartUDP: false,
WritePeers: false,
}
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)
ctxWCancel, cancel := context.WithCancel(ctx)
go server.peerAdder(ctxWCancel)
for i := 0; i < 10; i++ {
var msg *peerAddMsg
if strings.Contains(tt.name, "1 unique") {
msg = &peerAddMsg{
msg: &pb.ServerMessage{
Address: "1.1.1.1",
Port: "50051",
},
ping: false,
}
} else {
msg = &peerAddMsg{
msg: &pb.ServerMessage{
Address: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i),
Port: "50051",
},
ping: false,
}
}
server.peerChannel <- msg
}
// Have to give it a second to update peers since it's in
// another thread.
time.Sleep(time.Second)
got := len(server.Servers)
if got != tt.want {
t.Errorf("len(server.Servers) = %d, want %d", got, tt.want)
}
cancel()
})
}
}
// TestPeerWriter tests that the peerAdder goroutine writes the peer file
// properly when set to do so.
func TestPeerWriter(t *testing.T) {
ctx := context.Background()
args := &Args{
CmdType: ServeCmd,
Host: DefaultHost,
Port: DefaultPort,
EsHost: DefaultEsHost,
EsPort: DefaultEsPort,
UDPPort: DefaultUdpPort,
DisableEs: true,
PrometheusPort: DefaultPrometheusPort,
EsIndex: DefaultEsIndex,
Debug: true,
RefreshDelta: DefaultRefreshDelta,
CacheTTL: DefaultCacheTTL,
PeerFile: DefaultPeerFile,
Country: DefaultCountry,
StartPeerAdder: false,
StartPrometheus: false,
StartUDP: false,
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)
ctxWCancel, cancel := context.WithCancel(ctx)
go server.peerAdder(ctxWCancel)
for i := 0; i < 10; i++ {
var msg *peerAddMsg
if strings.Contains(tt.name, "1 unique") {
msg = &peerAddMsg{
msg: &pb.ServerMessage{
Address: "1.1.1.1",
Port: "50051",
},
ping: false,
}
} else {
msg = &peerAddMsg{
msg: &pb.ServerMessage{
Address: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i),
Port: "50051",
},
ping: false,
}
}
server.peerChannel <- msg
}
// Have to give it a second to update peers since it's in
// another thread.
time.Sleep(time.Second * 1)
got := lineCountFile(server.Args.PeerFile)
if got != tt.want {
t.Errorf("len(server.Servers) = %d, want %d", got, tt.want)
}
cancel()
})
}
removeFile(args.PeerFile)
}

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

@ -9,6 +9,7 @@ import (
"net/http"
"os"
"regexp"
"sync"
"time"
"github.com/ReneKroon/ttlcache/v2"
@ -27,39 +28,17 @@ type Server struct {
MultiSpaceRe *regexp.Regexp
WeirdCharsRe *regexp.Regexp
EsClient *elastic.Client
Servers []*FederatedServer
Servers map[string]*FederatedServer
QueryCache *ttlcache.Cache
S256 *hash.Hash
LastRefreshCheck time.Time
RefreshDelta time.Duration
NumESRefreshes int64
PeerSubs sync.Map
peerChannel chan *peerAddMsg
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,9 +83,14 @@ func getVersion() string {
'blockchain.address.unsubscribe'
*/
func MakeHubServer(args *Args) *Server {
// 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))
peerChannel := make(chan *peerAddMsg)
multiSpaceRe, err := regexp.Compile(`\s{2,}`)
if err != nil {
log.Fatal(err)
@ -116,23 +100,27 @@ 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)
servers := loadPeers(args)
var client *elastic.Client
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)
}
} else {
client = nil
}
cache := ttlcache.NewCache()
@ -157,34 +145,124 @@ func MakeHubServer(args *Args) *Server {
LastRefreshCheck: time.Now(),
RefreshDelta: refreshDelta,
NumESRefreshes: 0,
Servers: servers,
PeerSubs: sync.Map{},
peerChannel: peerChannel,
}
// Start up our background services
if args.StartPeerAdder {
go s.peerAdder(ctx)
}
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)
}
}()
}
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) {
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)
s.addPeer(&pb.ServerMessage{Address: host, Port: port}, false)
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) {
peer := &FederatedServer{
Address: in.Address,
Port: in.Port,
Ts: time.Now(),
}
return s.Servers[0], nil
s.PeerSubs.Store(peerKey(in), peer)
return &pb.StringValue{Value: "Success"}, nil
}
func (s *Server) Ping(context context.Context, args *pb.EmptyMessage) (*pb.StringValue, error) {
// PeerSubscribeStreaming is a streaming grpc endpoint that allows another hub to
// subscribe to this hub for peer updates. This first loops through all the
// peers that this hub knows about and sends them to the connecting hub,
// it then stores the peer, along with a channel to indicate when it's finished
// and it's stream context in our map of peer subs. Finally, it waits on the
// context to finish, or a message in the done channel. This function cannot
// exit while the peer is subscribe or the context will die. Communicating with
// the peer is handled by the peerAdder goroutine in federation.go, which
// listens on a channel of new peers and notifies all our connected peers when
// we find out about a new one.
func (s *Server) PeerSubscribeStreaming(in *pb.ServerMessage, stream pb.Hub_PeerSubscribeStreamingServer) error {
for _, server := range s.Servers {
err := stream.Send(&pb.ServerMessage{
Address: server.Address,
Port: server.Port,
})
if err != nil {
return err
}
}
done := make(chan bool)
ctx := stream.Context()
s.PeerSubs.Store(peerKey(in), &sub{stream: stream, done: done})
for {
select {
case <-done:
log.Println("Removing client:", in)
return nil
case <-ctx.Done():
log.Println("Client disconnected: ", in)
return nil
}
}
}
// 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) {
s.addPeer(args, true)
return &pb.StringValue{Value: "Success!"}, nil
}
// 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
}
}
}