diff --git a/app/appmessage/rpc_get_peer_addresses.go b/app/appmessage/rpc_get_peer_addresses.go index 7ad2f7868..a026e5396 100644 --- a/app/appmessage/rpc_get_peer_addresses.go +++ b/app/appmessage/rpc_get_peer_addresses.go @@ -20,7 +20,8 @@ func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage { // its respective RPC message type GetPeerAddressesResponseMessage struct { baseMessage - Addresses []*GetPeerAddressesKnownAddressMessage + Addresses []*GetPeerAddressesKnownAddressMessage + BannedAddresses []*GetPeerAddressesKnownAddressMessage Error *RPCError } @@ -31,9 +32,10 @@ func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand { } // NewGetPeerAddressesResponseMessage returns a instance of the message -func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage { +func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage, bannedAddresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage { return &GetPeerAddressesResponseMessage{ - Addresses: addresses, + Addresses: addresses, + BannedAddresses: bannedAddresses, } } diff --git a/app/component_manager.go b/app/component_manager.go index d99c930b2..a13ff0d29 100644 --- a/app/component_manager.go +++ b/app/component_manager.go @@ -72,11 +72,6 @@ func (a *ComponentManager) Stop() { log.Errorf("Error stopping the net adapter: %+v", err) } - err = a.addressManager.Stop() - if err != nil { - log.Errorf("Error stopping address manager: %s", err) - } - return } @@ -99,18 +94,22 @@ func NewComponentManager(cfg *config.Config, databaseContext *dbaccess.DatabaseC if err != nil { return nil, err } - addressManager, err := addressmanager.New(cfg, databaseContext) + + addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg)) if err != nil { return nil, err } + connectionManager, err := connmanager.New(cfg, netAdapter, addressManager) if err != nil { return nil, err } + protocolManager, err := protocol.NewManager(cfg, dag, netAdapter, addressManager, txMempool, connectionManager) if err != nil { return nil, err } + rpcManager := setupRPC(cfg, txMempool, dag, sigCache, netAdapter, protocolManager, connectionManager, addressManager, acceptanceIndex, interrupt) return &ComponentManager{ @@ -187,14 +186,14 @@ func (a *ComponentManager) maybeSeedFromDNS() { // Kaspad uses a lookup of the dns seeder here. Since seeder returns // IPs of nodes and not its own IP, we can not know real IP of // source. So we'll take first returned address as source. - a.addressManager.AddAddresses(addresses, addresses[0], nil) + a.addressManager.AddAddresses(addresses...) }) } if a.cfg.GRPCSeed != "" { dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil, func(addresses []*appmessage.NetAddress) { - a.addressManager.AddAddresses(addresses, addresses[0], nil) + a.addressManager.AddAddresses(addresses...) }) } } diff --git a/app/protocol/flows/addressexchange/receiveaddresses.go b/app/protocol/flows/addressexchange/receiveaddresses.go index 535f66725..6e52a835c 100644 --- a/app/protocol/flows/addressexchange/receiveaddresses.go +++ b/app/protocol/flows/addressexchange/receiveaddresses.go @@ -20,10 +20,6 @@ type ReceiveAddressesContext interface { func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route, peer *peerpkg.Peer) error { - if !context.AddressManager().NeedMoreAddresses() { - return nil - } - subnetworkID := peer.SubnetworkID() msgGetAddresses := appmessage.NewMsgRequestAddresses(false, subnetworkID) err := outgoingRoute.Enqueue(msgGetAddresses) @@ -51,7 +47,6 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou context.Config().SubnetworkID, msgAddresses.Command(), msgAddresses.SubnetworkID) } - sourceAddress := peer.Connection().NetAddress() - context.AddressManager().AddAddresses(msgAddresses.AddrList, sourceAddress, msgAddresses.SubnetworkID) + context.AddressManager().AddAddresses(msgAddresses.AddrList...) return nil } diff --git a/app/protocol/flows/addressexchange/sendaddresses.go b/app/protocol/flows/addressexchange/sendaddresses.go index 236073406..af6cd4128 100644 --- a/app/protocol/flows/addressexchange/sendaddresses.go +++ b/app/protocol/flows/addressexchange/sendaddresses.go @@ -1,10 +1,11 @@ package addressexchange import ( + "math/rand" + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" - "math/rand" ) // SendAddressesContext is the interface for the context needed for the SendAddresses flow. @@ -20,8 +21,7 @@ func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, ou } msgGetAddresses := message.(*appmessage.MsgRequestAddresses) - addresses := context.AddressManager().AddressCache(msgGetAddresses.IncludeAllSubnetworks, - msgGetAddresses.SubnetworkID) + addresses := context.AddressManager().Addresses() msgAddresses := appmessage.NewMsgAddresses(msgGetAddresses.IncludeAllSubnetworks, msgGetAddresses.SubnetworkID) err = msgAddresses.AddAddresses(shuffleAddresses(addresses)...) if err != nil { diff --git a/app/protocol/flows/handshake/handshake.go b/app/protocol/flows/handshake/handshake.go index 1319ea912..fc6d49f50 100644 --- a/app/protocol/flows/handshake/handshake.go +++ b/app/protocol/flows/handshake/handshake.go @@ -85,9 +85,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N } if peerAddress != nil { - subnetworkID := peer.SubnetworkID() - context.AddressManager().AddAddress(peerAddress, peerAddress, subnetworkID) - context.AddressManager().Good(peerAddress, subnetworkID) + context.AddressManager().AddAddresses(peerAddress) } context.StartIBDIfRequired() diff --git a/app/protocol/flows/handshake/sendversion.go b/app/protocol/flows/handshake/sendversion.go index c24d73e07..606ba328e 100644 --- a/app/protocol/flows/handshake/sendversion.go +++ b/app/protocol/flows/handshake/sendversion.go @@ -50,7 +50,7 @@ func (flow *sendVersionFlow) start() error { subnetworkID := flow.Config().SubnetworkID // Version message. - localAddress := flow.AddressManager().GetBestLocalAddress(flow.peer.Connection().NetAddress()) + localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress()) msg := appmessage.NewMsgVersion(localAddress, flow.NetAdapter().ID(), flow.Config().ActiveNetParams.Name, selectedTipHash, subnetworkID) msg.AddUserAgent(userAgentName, userAgentVersion, flow.Config().UserAgentComments...) diff --git a/app/rpc/rpchandlers/get_peer_addresses.go b/app/rpc/rpchandlers/get_peer_addresses.go index 849303442..9c7d19948 100644 --- a/app/rpc/rpchandlers/get_peer_addresses.go +++ b/app/rpc/rpchandlers/get_peer_addresses.go @@ -1,6 +1,9 @@ package rpchandlers import ( + "net" + "strconv" + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/rpc/rpccontext" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" @@ -8,14 +11,20 @@ import ( // HandleGetPeerAddresses handles the respectively named RPC command func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { - peersState, err := context.AddressManager.PeersStateForSerialization() - if err != nil { - return nil, err + netAddresses := context.AddressManager.Addresses() + addressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(netAddresses)) + for i, netAddress := range netAddresses { + addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10)) + addressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort} } - addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses)) - for i, address := range peersState.Addresses { - addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)} + + bannedAddresses := context.AddressManager.BannedAddresses() + bannedAddressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(bannedAddresses)) + for i, netAddress := range bannedAddresses { + addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10)) + bannedAddressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort} } - response := appmessage.NewGetPeerAddressesResponseMessage(addresses) + + response := appmessage.NewGetPeerAddressesResponseMessage(addressMessages, bannedAddressMessages) return response, nil } diff --git a/infrastructure/network/addressmanager/addressmanager.go b/infrastructure/network/addressmanager/addressmanager.go index c4e0faf6c..e78c321d6 100644 --- a/infrastructure/network/addressmanager/addressmanager.go +++ b/infrastructure/network/addressmanager/addressmanager.go @@ -5,1585 +5,225 @@ package addressmanager import ( - "bytes" - crand "crypto/rand" // for seeding "encoding/binary" - "encoding/gob" - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/config" - "github.com/kaspanet/kaspad/infrastructure/db/dbaccess" - "github.com/kaspanet/kaspad/util/mstime" - "github.com/pkg/errors" - "io" - "math/rand" - "net" - "runtime" - "strconv" - "strings" "sync" - "sync/atomic" - "time" - "github.com/kaspanet/kaspad/util/subnetworkid" - - "github.com/kaspanet/kaspad/util/daghash" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/pkg/errors" ) -// AddressKey represents a "string" key in the form of ip:port for IPv4 addresses -// or [ip]:port for IPv6 addresses for use as keys in maps. +// AddressRandomizer is the interface for the randomizer needed for the AddressManager. +type AddressRandomizer interface { + RandomAddress(addresses []*appmessage.NetAddress) *appmessage.NetAddress + RandomAddresses(addresses []*appmessage.NetAddress, count int) []*appmessage.NetAddress +} + +// AddressKey represents a "string" key of the ip addresses +// for use as keys in maps. type AddressKey string -type newAddressBucketArray [NewBucketCount]map[AddressKey]*KnownAddress -type triedAddressBucketArray [TriedBucketCount][]*KnownAddress - -// AddressManager provides a concurrency safe address manager for caching potential -// peers on the Kaspa network. -type AddressManager struct { - cfg *config.Config - databaseContext *dbaccess.DatabaseContext - - mutex sync.Mutex - lookupFunc func(string) ([]net.IP, error) - random *rand.Rand - key [32]byte - addressIndex map[AddressKey]*KnownAddress // address keys to known addresses for all addresses. - started int32 - shutdown int32 - wg sync.WaitGroup - quit chan struct{} - localAddressesLock sync.Mutex - localAddresses map[AddressKey]*localAddress - localSubnetworkID *subnetworkid.SubnetworkID - - fullNodeNewAddressBucketArray newAddressBucketArray - fullNodeNewAddressCount int - fullNodeTriedAddressBucketArray triedAddressBucketArray - fullNodeTriedAddressCount int - subnetworkNewAddressBucketArrays map[subnetworkid.SubnetworkID]*newAddressBucketArray - subnetworkNewAddressCounts map[subnetworkid.SubnetworkID]int - subnetworkTriedAddresBucketArrays map[subnetworkid.SubnetworkID]*triedAddressBucketArray - subnetworkTriedAddressCounts map[subnetworkid.SubnetworkID]int -} - -type serializedKnownAddress struct { - Address AddressKey - SourceAddress AddressKey - SubnetworkID string - Attempts int - TimeStamp int64 - LastAttempt int64 - LastSuccess int64 - IsBanned bool - BannedTime int64 - // no refcount or tried, that is available from context. -} - -type serializedNewAddressBucketArray [NewBucketCount][]AddressKey -type serializedTriedAddressBucketArray [TriedBucketCount][]AddressKey - -// PeersStateForSerialization is the data model that is used to -// serialize the peers state to any encoding. -type PeersStateForSerialization struct { - Version int - Key [32]byte - Addresses []*serializedKnownAddress - - SubnetworkNewAddressBucketArrays map[string]*serializedNewAddressBucketArray // string is Subnetwork ID - FullNodeNewAddressBucketArray serializedNewAddressBucketArray - SubnetworkTriedAddressBucketArrays map[string]*serializedTriedAddressBucketArray // string is Subnetwork ID - FullNodeTriedAddressBucketArray serializedTriedAddressBucketArray -} - -type localAddress struct { - netAddress *appmessage.NetAddress - score AddressPriority -} - -// AddressPriority type is used to describe the hierarchy of local address -// discovery methods. -type AddressPriority int - -const ( - // InterfacePrio signifies the address is on a local interface - InterfacePrio AddressPriority = iota - - // BoundPrio signifies the address has been explicitly bounded to. - BoundPrio - - // UpnpPrio signifies the address was obtained from UPnP. - UpnpPrio - - // HTTPPrio signifies the address was obtained from an external HTTP service. - HTTPPrio - - // ManualPrio signifies the address was provided by --externalip. - ManualPrio -) - -const ( - // needAddressThreshold is the number of addresses under which the - // address manager will claim to need more addresses. - needAddressThreshold = 1000 - - // dumpAddressInterval is the interval used to dump the address - // cache to disk for future use. - dumpAddressInterval = time.Minute * 10 - - // triedBucketSize is the maximum number of addresses in each - // tried address bucket. - triedBucketSize = 256 - - // TriedBucketCount is the number of buckets we split tried - // addresses over. - TriedBucketCount = 64 - - // newBucketSize is the maximum number of addresses in each new address - // bucket. - newBucketSize = 64 - - // NewBucketCount is the number of buckets that we spread new addresses - // over. - NewBucketCount = 1024 - - // triedBucketsPerGroup is the number of tried buckets over which an - // address group will be spread. - triedBucketsPerGroup = 8 - - // newBucketsPerGroup is the number of new buckets over which an - // source address group will be spread. - newBucketsPerGroup = 64 - - // newBucketsPerAddress is the number of buckets a frequently seen new - // address may end up in. - newBucketsPerAddress = 8 - - // numMissingDays is the number of days before which we assume an - // address has vanished if we have not seen it announced in that long. - numMissingDays = 30 - - // numRetries is the number of tried without a single success before - // we assume an address is bad. - numRetries = 3 - - // maxFailures is the maximum number of failures we will accept without - // a success before considering an address bad. - maxFailures = 10 - - // minBadDays is the number of days since the last success before we - // will consider evicting an address. - minBadDays = 7 - - // getAddrMin is the least addresses that we will send in response - // to a getAddresses. If we have less than this amount, we send everything. - getAddrMin = 50 - - // GetAddressesMax is the most addresses that we will send in response - // to a getAddress (in practise the most addresses we will return from a - // call to AddressCache()). - GetAddressesMax = 2500 - - // getAddrPercent is the percentage of total addresses known that we - // will share with a call to AddressCache. - getAddrPercent = 23 - - // serializationVersion is the current version of the on-disk format. - serializationVersion = 1 -) // ErrAddressNotFound is an error returned from some functions when a // given address is not found in the address manager var ErrAddressNotFound = errors.New("address not found") +// NetAddressKey returns a key of the ip address to use it in maps. +func netAddressKey(netAddress *appmessage.NetAddress) AddressKey { + port := make([]byte, 2, 2) + binary.LittleEndian.PutUint16(port, netAddress.Port) + + key := make([]byte, len(netAddress.IP), len(netAddress.IP)+len(port)) + copy(key, netAddress.IP) + + return AddressKey(append(key, port...)) +} + +// netAddressKeys returns a key of the ip address to use it in maps. +func netAddressesKeys(netAddresses []*appmessage.NetAddress) map[AddressKey]bool { + result := make(map[AddressKey]bool, len(netAddresses)) + for _, netAddress := range netAddresses { + key := netAddressKey(netAddress) + result[key] = true + } + + return result +} + +// AddressManager provides a concurrency safe address manager for caching potential +// peers on the Kaspa network. +type AddressManager struct { + addresses map[AddressKey]*appmessage.NetAddress + bannedAddresses map[AddressKey]*appmessage.NetAddress + localAddresses *localAddressManager + mutex sync.Mutex + cfg *Config + random AddressRandomizer +} + // New returns a new Kaspa address manager. -func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext) (*AddressManager, error) { - addressManager := AddressManager{ - cfg: cfg, - databaseContext: databaseContext, - lookupFunc: cfg.Lookup, - random: rand.New(rand.NewSource(time.Now().UnixNano())), - quit: make(chan struct{}), - localAddresses: make(map[AddressKey]*localAddress), - localSubnetworkID: cfg.SubnetworkID, - } - err := addressManager.initListeners() - if err != nil { - return nil, err - } - addressManager.reset() - return &addressManager, nil -} - -// updateAddress is a helper function to either update an address already known -// to the address manager, or to add the address if not already known. -func (am *AddressManager) updateAddress(netAddress, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) { - // Filter out non-routable addresses. Note that non-routable - // also includes invalid and local addresses. - if !am.IsRoutable(netAddress) { - return - } - - addressKey := NetAddressKey(netAddress) - knownAddress := am.knownAddress(netAddress) - if knownAddress != nil { - // Update the last seen time and services. - // note that to prevent causing excess garbage on getaddr - // messages the netaddresses in addrmaanger are *immutable*, - // if we need to change them then we replace the pointer with a - // new copy so that we don't have to copy every netAddress for getaddress. - if netAddress.Timestamp.After(knownAddress.netAddress.Timestamp) || - (knownAddress.netAddress.Services&netAddress.Services) != - netAddress.Services { - - netAddressCopy := *knownAddress.netAddress - netAddressCopy.Timestamp = netAddress.Timestamp - netAddressCopy.AddService(netAddress.Services) - knownAddress.netAddress = &netAddressCopy - } - - // If already in tried, we have nothing to do here. - if knownAddress.tried { - return - } - - // Already at our max? - if knownAddress.referenceCount == newBucketsPerAddress { - return - } - - // The more entries we have, the less likely we are to add more. - // likelihood is 2N. - factor := int32(2 * knownAddress.referenceCount) - if am.random.Int31n(factor) != 0 { - return - } - } else { - // Make a copy of the net address to avoid races since it is - // updated elsewhere in the addressManager code and would otherwise - // change the actual netAddress on the peer. - netAddressCopy := *netAddress - knownAddress = &KnownAddress{netAddress: &netAddressCopy, sourceAddress: sourceAddress, subnetworkID: subnetworkID} - am.addressIndex[addressKey] = knownAddress - am.incrementNewAddressCount(subnetworkID) - } - - // Already exists? - newAddressBucketArray := am.newAddressBucketArray(knownAddress.subnetworkID) - newAddressBucketIndex := am.newAddressBucketIndex(netAddress, sourceAddress) - if newAddressBucketArray != nil { - if _, ok := newAddressBucketArray[newAddressBucketIndex][addressKey]; ok { - return - } - } - - // Enforce max addresses. - if newAddressBucketArray != nil && len(newAddressBucketArray[newAddressBucketIndex]) > newBucketSize { - log.Tracef("new bucket is full, expiring old") - am.expireNew(knownAddress.subnetworkID, newAddressBucketIndex) - } - - // Add to new bucket. - knownAddress.referenceCount++ - am.updateAddrNew(newAddressBucketIndex, addressKey, knownAddress) - - totalAddressCount := am.newAddressCount(knownAddress.subnetworkID) + am.triedAddressCount(knownAddress.subnetworkID) - log.Tracef("Added new address %s for a total of %d addresses", addressKey, totalAddressCount) - -} - -func (am *AddressManager) updateAddrNew(bucket int, addressKey AddressKey, knownAddress *KnownAddress) { - if knownAddress.subnetworkID == nil { - am.fullNodeNewAddressBucketArray[bucket][addressKey] = knownAddress - return - } - - if _, ok := am.subnetworkNewAddressBucketArrays[*knownAddress.subnetworkID]; !ok { - am.subnetworkNewAddressBucketArrays[*knownAddress.subnetworkID] = &newAddressBucketArray{} - for i := range am.subnetworkNewAddressBucketArrays[*knownAddress.subnetworkID] { - am.subnetworkNewAddressBucketArrays[*knownAddress.subnetworkID][i] = make(map[AddressKey]*KnownAddress) - } - } - am.subnetworkNewAddressBucketArrays[*knownAddress.subnetworkID][bucket][addressKey] = knownAddress -} - -func (am *AddressManager) updateAddrTried(bucketIndex int, knownAddress *KnownAddress) { - if knownAddress.subnetworkID == nil { - am.fullNodeTriedAddressBucketArray[bucketIndex] = append(am.fullNodeTriedAddressBucketArray[bucketIndex], knownAddress) - return - } - - if _, ok := am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID]; !ok { - am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID] = &triedAddressBucketArray{} - for i := range am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID] { - am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID][i] = nil - } - } - am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID][bucketIndex] = append(am.subnetworkTriedAddresBucketArrays[*knownAddress.subnetworkID][bucketIndex], knownAddress) -} - -// expireNew makes space in the new buckets by expiring the really bad entries. -// If no bad entries are available we look at a few and remove the oldest. -func (am *AddressManager) expireNew(subnetworkID *subnetworkid.SubnetworkID, bucketIndex int) { - // First see if there are any entries that are so bad we can just throw - // them away. otherwise we throw away the oldest entry in the cache. - // We keep track of oldest in the initial traversal and use that - // information instead. - var oldest *KnownAddress - newAddressBucketArray := am.newAddressBucketArray(subnetworkID) - for addressKey, knownAddress := range newAddressBucketArray[bucketIndex] { - if knownAddress.isBad() { - log.Tracef("expiring bad address %s", addressKey) - delete(newAddressBucketArray[bucketIndex], addressKey) - knownAddress.referenceCount-- - if knownAddress.referenceCount == 0 { - am.decrementNewAddressCount(subnetworkID) - delete(am.addressIndex, addressKey) - } - continue - } - if oldest == nil { - oldest = knownAddress - } else if !knownAddress.netAddress.Timestamp.After(oldest.netAddress.Timestamp) { - oldest = knownAddress - } - } - - if oldest != nil { - addressKey := NetAddressKey(oldest.netAddress) - log.Tracef("expiring oldest address %s", addressKey) - - delete(newAddressBucketArray[bucketIndex], addressKey) - oldest.referenceCount-- - if oldest.referenceCount == 0 { - am.decrementNewAddressCount(subnetworkID) - delete(am.addressIndex, addressKey) - } - } -} - -// pickTried selects an address from the tried bucket to be evicted. -// We just choose the eldest. -func (am *AddressManager) pickTried(subnetworkID *subnetworkid.SubnetworkID, bucketIndex int) ( - knownAddress *KnownAddress, knownAddressIndex int) { - - var oldest *KnownAddress - oldestIndex := -1 - triedAddressBucketArray := am.triedAddressBucketArray(subnetworkID) - for i, address := range triedAddressBucketArray[bucketIndex] { - if oldest == nil || oldest.netAddress.Timestamp.After(address.netAddress.Timestamp) { - oldestIndex = i - oldest = address - } - } - return oldest, oldestIndex -} - -func (am *AddressManager) newAddressBucketIndex(netAddress, srcAddress *appmessage.NetAddress) int { - // doublesha256(key + sourcegroup + int64(doublesha256(key + group + sourcegroup))%bucket_per_source_group) % num_new_buckets - - data1 := []byte{} - data1 = append(data1, am.key[:]...) - data1 = append(data1, []byte(am.GroupKey(netAddress))...) - data1 = append(data1, []byte(am.GroupKey(srcAddress))...) - hash1 := daghash.DoubleHashB(data1) - hash64 := binary.LittleEndian.Uint64(hash1) - hash64 %= newBucketsPerGroup - var hashbuf [8]byte - binary.LittleEndian.PutUint64(hashbuf[:], hash64) - data2 := []byte{} - data2 = append(data2, am.key[:]...) - data2 = append(data2, am.GroupKey(srcAddress)...) - data2 = append(data2, hashbuf[:]...) - - hash2 := daghash.DoubleHashB(data2) - return int(binary.LittleEndian.Uint64(hash2) % NewBucketCount) -} - -func (am *AddressManager) triedAddressBucketIndex(netAddress *appmessage.NetAddress) int { - // doublesha256(key + group + truncate_to_64bits(doublesha256(key)) % buckets_per_group) % num_buckets - data1 := []byte{} - data1 = append(data1, am.key[:]...) - data1 = append(data1, []byte(NetAddressKey(netAddress))...) - hash1 := daghash.DoubleHashB(data1) - hash64 := binary.LittleEndian.Uint64(hash1) - hash64 %= triedBucketsPerGroup - var hashbuf [8]byte - binary.LittleEndian.PutUint64(hashbuf[:], hash64) - data2 := []byte{} - data2 = append(data2, am.key[:]...) - data2 = append(data2, am.GroupKey(netAddress)...) - data2 = append(data2, hashbuf[:]...) - - hash2 := daghash.DoubleHashB(data2) - return int(binary.LittleEndian.Uint64(hash2) % TriedBucketCount) -} - -// addressHandler is the main handler for the address manager. It must be run -// as a goroutine. -func (am *AddressManager) addressHandler() { - dumpAddressTicker := time.NewTicker(dumpAddressInterval) - defer dumpAddressTicker.Stop() - -out: - for { - select { - case <-dumpAddressTicker.C: - err := am.savePeers() - if err != nil { - panic(errors.Wrap(err, "error saving peers")) - } - - case <-am.quit: - break out - } - } - err := am.savePeers() - if err != nil { - panic(errors.Wrap(err, "error saving peers")) - } - am.wg.Done() - log.Trace("Address handler done") -} - -// savePeers saves all the known addresses to the database so they can be read back -// in at next run. -func (am *AddressManager) savePeers() error { - serializedPeersState, err := am.serializePeersState() - if err != nil { - return err - } - - return dbaccess.StorePeersState(am.databaseContext, serializedPeersState) -} - -func (am *AddressManager) serializePeersState() ([]byte, error) { - peersState, err := am.PeersStateForSerialization() +func New(cfg *Config) (*AddressManager, error) { + localAddresses, err := newLocalAddressManager(cfg) if err != nil { return nil, err } - buffer := &bytes.Buffer{} - encoder := gob.NewEncoder(buffer) - err = encoder.Encode(&peersState) - if err != nil { - return nil, errors.Wrap(err, "failed to encode peers state") - } - - return buffer.Bytes(), nil + return &AddressManager{ + addresses: map[AddressKey]*appmessage.NetAddress{}, + bannedAddresses: map[AddressKey]*appmessage.NetAddress{}, + localAddresses: localAddresses, + random: NewAddressRandomize(), + cfg: cfg, + }, nil } -// PeersStateForSerialization returns the data model that is used to serialize the peers state to any encoding. -func (am *AddressManager) PeersStateForSerialization() (*PeersStateForSerialization, error) { +func (am *AddressManager) addAddressNoLock(address *appmessage.NetAddress) { + if !IsRoutable(address, am.cfg.AcceptUnroutable) { + return + } + + key := netAddressKey(address) + _, ok := am.addresses[key] + if !ok { + am.addresses[key] = address + } +} + +// AddAddress adds address to the address manager +func (am *AddressManager) AddAddress(address *appmessage.NetAddress) { am.mutex.Lock() defer am.mutex.Unlock() - // First we make a serializable data structure so we can encode it to - // gob. - peersState := new(PeersStateForSerialization) - peersState.Version = serializationVersion - copy(peersState.Key[:], am.key[:]) - - peersState.Addresses = make([]*serializedKnownAddress, len(am.addressIndex)) - i := 0 - for addressKey, knownAddress := range am.addressIndex { - serializedAddress := new(serializedKnownAddress) - serializedAddress.Address = addressKey - if knownAddress.subnetworkID == nil { - serializedAddress.SubnetworkID = "" - } else { - serializedAddress.SubnetworkID = knownAddress.subnetworkID.String() - } - serializedAddress.TimeStamp = knownAddress.netAddress.Timestamp.UnixMilliseconds() - serializedAddress.SourceAddress = NetAddressKey(knownAddress.sourceAddress) - serializedAddress.Attempts = knownAddress.attempts - serializedAddress.LastAttempt = knownAddress.lastAttempt.UnixMilliseconds() - serializedAddress.LastSuccess = knownAddress.lastSuccess.UnixMilliseconds() - serializedAddress.IsBanned = knownAddress.isBanned - serializedAddress.BannedTime = knownAddress.bannedTime.UnixMilliseconds() - // Tried and referenceCount are implicit in the rest of the structure - // and will be worked out from context on unserialisation. - peersState.Addresses[i] = serializedAddress - i++ - } - - peersState.SubnetworkNewAddressBucketArrays = make(map[string]*serializedNewAddressBucketArray) - for subnetworkID := range am.subnetworkNewAddressBucketArrays { - subnetworkIDStr := subnetworkID.String() - peersState.SubnetworkNewAddressBucketArrays[subnetworkIDStr] = &serializedNewAddressBucketArray{} - - for i := range am.subnetworkNewAddressBucketArrays[subnetworkID] { - peersState.SubnetworkNewAddressBucketArrays[subnetworkIDStr][i] = make([]AddressKey, len(am.subnetworkNewAddressBucketArrays[subnetworkID][i])) - j := 0 - for k := range am.subnetworkNewAddressBucketArrays[subnetworkID][i] { - peersState.SubnetworkNewAddressBucketArrays[subnetworkIDStr][i][j] = k - j++ - } - } - } - - for i := range am.fullNodeNewAddressBucketArray { - peersState.FullNodeNewAddressBucketArray[i] = make([]AddressKey, len(am.fullNodeNewAddressBucketArray[i])) - j := 0 - for k := range am.fullNodeNewAddressBucketArray[i] { - peersState.FullNodeNewAddressBucketArray[i][j] = k - j++ - } - } - - peersState.SubnetworkTriedAddressBucketArrays = make(map[string]*serializedTriedAddressBucketArray) - for subnetworkID := range am.subnetworkTriedAddresBucketArrays { - subnetworkIDStr := subnetworkID.String() - peersState.SubnetworkTriedAddressBucketArrays[subnetworkIDStr] = &serializedTriedAddressBucketArray{} - - for i := range am.subnetworkTriedAddresBucketArrays[subnetworkID] { - peersState.SubnetworkTriedAddressBucketArrays[subnetworkIDStr][i] = make([]AddressKey, len(am.subnetworkTriedAddresBucketArrays[subnetworkID][i])) - j := 0 - for _, knownAddress := range am.subnetworkTriedAddresBucketArrays[subnetworkID][i] { - peersState.SubnetworkTriedAddressBucketArrays[subnetworkIDStr][i][j] = NetAddressKey(knownAddress.netAddress) - j++ - } - } - } - - for i := range am.fullNodeTriedAddressBucketArray { - peersState.FullNodeTriedAddressBucketArray[i] = make([]AddressKey, len(am.fullNodeTriedAddressBucketArray[i])) - j := 0 - for _, knownAddress := range am.fullNodeTriedAddressBucketArray[i] { - peersState.FullNodeTriedAddressBucketArray[i][j] = NetAddressKey(knownAddress.netAddress) - j++ - } - } - - return peersState, nil + am.addAddressNoLock(address) } -// loadPeers loads the known address from the database. If missing, -// just don't load anything and start fresh. -func (am *AddressManager) loadPeers() error { - am.mutex.Lock() - defer am.mutex.Unlock() - - serializedPeerState, err := dbaccess.FetchPeersState(am.databaseContext) - if dbaccess.IsNotFoundError(err) { - am.reset() - log.Info("No peers state was found in the database. Created a new one", am.totalNumAddresses()) - return nil - } - if err != nil { - return err - } - - err = am.deserializePeersState(serializedPeerState) - if err != nil { - return err - } - - log.Infof("Loaded %d addresses from database", am.totalNumAddresses()) - return nil -} - -func (am *AddressManager) deserializePeersState(serializedPeerState []byte) error { - var peersState PeersStateForSerialization - r := bytes.NewBuffer(serializedPeerState) - dec := gob.NewDecoder(r) - err := dec.Decode(&peersState) - if err != nil { - return errors.Wrap(err, "error deserializing peers state") - } - - if peersState.Version != serializationVersion { - return errors.Errorf("unknown version %d in serialized "+ - "peers state", peersState.Version) - } - copy(am.key[:], peersState.Key[:]) - - for _, serializedKnownAddress := range peersState.Addresses { - knownAddress := new(KnownAddress) - knownAddress.netAddress, err = am.DeserializeNetAddress(serializedKnownAddress.Address) - if err != nil { - return errors.Errorf("failed to deserialize netaddress "+ - "%s: %s", serializedKnownAddress.Address, err) - } - knownAddress.sourceAddress, err = am.DeserializeNetAddress(serializedKnownAddress.SourceAddress) - if err != nil { - return errors.Errorf("failed to deserialize netaddress "+ - "%s: %s", serializedKnownAddress.SourceAddress, err) - } - if serializedKnownAddress.SubnetworkID != "" { - knownAddress.subnetworkID, err = subnetworkid.NewFromStr(serializedKnownAddress.SubnetworkID) - if err != nil { - return errors.Errorf("failed to deserialize subnetwork id "+ - "%s: %s", serializedKnownAddress.SubnetworkID, err) - } - } - knownAddress.attempts = serializedKnownAddress.Attempts - knownAddress.lastAttempt = mstime.UnixMilliseconds(serializedKnownAddress.LastAttempt) - knownAddress.lastSuccess = mstime.UnixMilliseconds(serializedKnownAddress.LastSuccess) - knownAddress.isBanned = serializedKnownAddress.IsBanned - knownAddress.bannedTime = mstime.UnixMilliseconds(serializedKnownAddress.BannedTime) - am.addressIndex[NetAddressKey(knownAddress.netAddress)] = knownAddress - } - - for subnetworkIDStr := range peersState.SubnetworkNewAddressBucketArrays { - subnetworkID, err := subnetworkid.NewFromStr(subnetworkIDStr) - if err != nil { - return err - } - for i, subnetworkNewAddressBucket := range peersState.SubnetworkNewAddressBucketArrays[subnetworkIDStr] { - for _, addressKey := range subnetworkNewAddressBucket { - knownAddress, ok := am.addressIndex[addressKey] - if !ok { - return errors.Errorf("newbucket contains %s but "+ - "none in address list", addressKey) - } - - if knownAddress.referenceCount == 0 { - am.subnetworkNewAddressCounts[*subnetworkID]++ - } - knownAddress.referenceCount++ - am.updateAddrNew(i, addressKey, knownAddress) - } - } - } - - for i, fullNodeNewAddressBucket := range peersState.FullNodeNewAddressBucketArray { - for _, addressKey := range fullNodeNewAddressBucket { - knownAddress, ok := am.addressIndex[addressKey] - if !ok { - return errors.Errorf("full nodes newbucket contains %s but "+ - "none in address list", addressKey) - } - - if knownAddress.referenceCount == 0 { - am.fullNodeNewAddressCount++ - } - knownAddress.referenceCount++ - am.updateAddrNew(i, addressKey, knownAddress) - } - } - - for subnetworkIDString := range peersState.SubnetworkTriedAddressBucketArrays { - subnetworkID, err := subnetworkid.NewFromStr(subnetworkIDString) - if err != nil { - return err - } - for i, subnetworkTriedAddressBucket := range peersState.SubnetworkTriedAddressBucketArrays[subnetworkIDString] { - for _, addressKey := range subnetworkTriedAddressBucket { - knownAddress, ok := am.addressIndex[addressKey] - if !ok { - return errors.Errorf("Tried bucket contains %s but "+ - "none in address list", addressKey) - } - - knownAddress.tried = true - am.subnetworkTriedAddressCounts[*subnetworkID]++ - am.subnetworkTriedAddresBucketArrays[*subnetworkID][i] = append(am.subnetworkTriedAddresBucketArrays[*subnetworkID][i], knownAddress) - } - } - } - - for i, fullNodeTriedAddressBucket := range peersState.FullNodeTriedAddressBucketArray { - for _, addressKey := range fullNodeTriedAddressBucket { - knownAddress, ok := am.addressIndex[addressKey] - if !ok { - return errors.Errorf("Full nodes tried bucket contains %s but "+ - "none in address list", addressKey) - } - - knownAddress.tried = true - am.fullNodeTriedAddressCount++ - am.fullNodeTriedAddressBucketArray[i] = append(am.fullNodeTriedAddressBucketArray[i], knownAddress) - } - } - - // Sanity checking. - for addressKey, knownAddress := range am.addressIndex { - if knownAddress.referenceCount == 0 && !knownAddress.tried { - return errors.Errorf("address %s after serialisation "+ - "with no references", addressKey) - } - - if knownAddress.referenceCount > 0 && knownAddress.tried { - return errors.Errorf("address %s after serialisation "+ - "which is both new and tried!", addressKey) - } - } - - return nil -} - -// DeserializeNetAddress converts a given address string to a *appmessage.NetAddress -func (am *AddressManager) DeserializeNetAddress(addressKey AddressKey) (*appmessage.NetAddress, error) { - host, portString, err := net.SplitHostPort(string(addressKey)) - if err != nil { - return nil, err - } - port, err := strconv.ParseUint(portString, 10, 16) - if err != nil { - return nil, err - } - - return am.HostToNetAddress(host, uint16(port), appmessage.SFNodeNetwork) -} - -// Start begins the core address handler which manages a pool of known -// addresses, timeouts, and interval based writes. -func (am *AddressManager) Start() error { - // Already started? - if atomic.AddInt32(&am.started, 1) != 1 { - return nil - } - - log.Trace("Starting address manager") - - // Load peers we already know about from the database. - err := am.loadPeers() - if err != nil { - return err - } - - // Start the address ticker to save addresses periodically. - am.wg.Add(1) - spawn("addressManager.addressHandler", am.addressHandler) - return nil -} - -// Stop gracefully shuts down the address manager by stopping the main handler. -func (am *AddressManager) Stop() error { - if atomic.AddInt32(&am.shutdown, 1) != 1 { - log.Warnf("Address manager is already in the process of " + - "shutting down") - return nil - } - - log.Infof("Address manager shutting down") - close(am.quit) - am.wg.Wait() - return nil -} - -// AddAddresses adds new addresses to the address manager. It enforces a max -// number of addresses and silently ignores duplicate addresses. It is -// safe for concurrent access. -func (am *AddressManager) AddAddresses(addresses []*appmessage.NetAddress, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) { +// AddAddresses adds addresses to the address manager +func (am *AddressManager) AddAddresses(addresses ...*appmessage.NetAddress) { am.mutex.Lock() defer am.mutex.Unlock() for _, address := range addresses { - am.updateAddress(address, sourceAddress, subnetworkID) + am.addAddressNoLock(address) } } -// AddAddress adds a new address to the address manager. It enforces a max -// number of addresses and silently ignores duplicate addresses. It is -// safe for concurrent access. -func (am *AddressManager) AddAddress(address, sourceAddress *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) { +// RemoveAddress removes addresses from the address manager +func (am *AddressManager) RemoveAddress(address *appmessage.NetAddress) { am.mutex.Lock() defer am.mutex.Unlock() - am.updateAddress(address, sourceAddress, subnetworkID) + key := netAddressKey(address) + delete(am.addresses, key) + delete(am.bannedAddresses, key) } -// numAddresses returns the number of addresses that belongs to a specific subnetwork id -// which are known to the address manager. -func (am *AddressManager) numAddresses(subnetworkID *subnetworkid.SubnetworkID) int { - if subnetworkID == nil { - return am.fullNodeNewAddressCount + am.fullNodeTriedAddressCount - } - return am.subnetworkTriedAddressCounts[*subnetworkID] + am.subnetworkNewAddressCounts[*subnetworkID] -} - -// totalNumAddresses returns the number of addresses known to the address manager. -func (am *AddressManager) totalNumAddresses() int { - total := am.fullNodeNewAddressCount + am.fullNodeTriedAddressCount - for _, numAddresses := range am.subnetworkTriedAddressCounts { - total += numAddresses - } - for _, numAddresses := range am.subnetworkNewAddressCounts { - total += numAddresses - } - return total -} - -// TotalNumAddresses returns the number of addresses known to the address manager. -func (am *AddressManager) TotalNumAddresses() int { +// Addresses returns all addresses +func (am *AddressManager) Addresses() []*appmessage.NetAddress { am.mutex.Lock() defer am.mutex.Unlock() - return am.totalNumAddresses() + result := make([]*appmessage.NetAddress, 0, len(am.addresses)) + for _, address := range am.addresses { + result = append(result, address) + } + + return result } -// NeedMoreAddresses returns whether or not the address manager needs more -// addresses. -func (am *AddressManager) NeedMoreAddresses() bool { +// BannedAddresses returns all banned addresses +func (am *AddressManager) BannedAddresses() []*appmessage.NetAddress { am.mutex.Lock() defer am.mutex.Unlock() - allAddresses := am.numAddresses(am.localSubnetworkID) - if am.localSubnetworkID != nil { - allAddresses += am.numAddresses(nil) + result := make([]*appmessage.NetAddress, 0, len(am.bannedAddresses)) + for _, address := range am.bannedAddresses { + result = append(result, address) } - return allAddresses < needAddressThreshold + + return result } -// AddressCache returns the current address cache. It must be treated as -// read-only (but since it is a copy now, this is not as dangerous). -func (am *AddressManager) AddressCache(includeAllSubnetworks bool, subnetworkID *subnetworkid.SubnetworkID) []*appmessage.NetAddress { +// NotBannedAddressesWithException returns all not banned addresses with excpetion +func (am *AddressManager) NotBannedAddressesWithException(exceptions []*appmessage.NetAddress) []*appmessage.NetAddress { + exceptionsKeys := netAddressesKeys(exceptions) am.mutex.Lock() defer am.mutex.Unlock() - if len(am.addressIndex) == 0 { - return nil - } - - allAddresses := []*appmessage.NetAddress{} - // Iteration order is undefined here, but we randomise it anyway. - for _, v := range am.addressIndex { - if includeAllSubnetworks || v.SubnetworkID().IsEqual(subnetworkID) { - allAddresses = append(allAddresses, v.netAddress) + result := make([]*appmessage.NetAddress, 0, len(am.addresses)-len(exceptions)) + for key, address := range am.addresses { + if !exceptionsKeys[key] { + result = append(result, address) } } - numAddresses := len(allAddresses) * getAddrPercent / 100 - if numAddresses > GetAddressesMax { - numAddresses = GetAddressesMax - } - if len(allAddresses) < getAddrMin { - numAddresses = len(allAddresses) - } - if len(allAddresses) > getAddrMin && numAddresses < getAddrMin { - numAddresses = getAddrMin - } - - // Fisher-Yates shuffle the array. We only need to do the first - // `numAddresses' since we are throwing the rest. - for i := 0; i < numAddresses; i++ { - // pick a number between current index and the end - j := rand.Intn(len(allAddresses)-i) + i - allAddresses[i], allAddresses[j] = allAddresses[j], allAddresses[i] - } - - // slice off the limit we are willing to share. - return allAddresses[0:numAddresses] + return result } -// reset resets the address manager by reinitialising the random source -// and allocating fresh empty bucket storage. -func (am *AddressManager) reset() { - am.addressIndex = make(map[AddressKey]*KnownAddress) - - // fill key with bytes from a good random source. - io.ReadFull(crand.Reader, am.key[:]) - am.subnetworkNewAddressBucketArrays = make(map[subnetworkid.SubnetworkID]*newAddressBucketArray) - am.subnetworkTriedAddresBucketArrays = make(map[subnetworkid.SubnetworkID]*triedAddressBucketArray) - - am.subnetworkNewAddressCounts = make(map[subnetworkid.SubnetworkID]int) - am.subnetworkTriedAddressCounts = make(map[subnetworkid.SubnetworkID]int) - - for i := range am.fullNodeNewAddressBucketArray { - am.fullNodeNewAddressBucketArray[i] = make(map[AddressKey]*KnownAddress) - } - for i := range am.fullNodeTriedAddressBucketArray { - am.fullNodeTriedAddressBucketArray[i] = nil - } - am.fullNodeNewAddressCount = 0 - am.fullNodeTriedAddressCount = 0 +// RandomAddress returns a random address that isn't banned and isn't in exceptions +func (am *AddressManager) RandomAddress(exceptions []*appmessage.NetAddress) *appmessage.NetAddress { + validAddresses := am.NotBannedAddressesWithException(exceptions) + return am.random.RandomAddress(validAddresses) } -// HostToNetAddress returns a netaddress given a host address. If -// the host is not an IP address it will be resolved. -func (am *AddressManager) HostToNetAddress(host string, port uint16, services appmessage.ServiceFlag) (*appmessage.NetAddress, error) { - ip := net.ParseIP(host) - if ip == nil { - ips, err := am.lookupFunc(host) - if err != nil { - return nil, err - } - if len(ips) == 0 { - return nil, errors.Errorf("no addresses found for %s", host) - } - ip = ips[0] - } - - return appmessage.NewNetAddressIPPort(ip, port, services), nil +// RandomAddresses returns count addresses at random that aren't banned and aren't in exceptions +func (am *AddressManager) RandomAddresses(count int, exceptions []*appmessage.NetAddress) []*appmessage.NetAddress { + validAddresses := am.NotBannedAddressesWithException(exceptions) + return am.random.RandomAddresses(validAddresses, count) } -// NetAddressKey returns a key in the form of ip:port for IPv4 addresses -// or [ip]:port for IPv6 addresses for use as keys in maps. -func NetAddressKey(netAddress *appmessage.NetAddress) AddressKey { - port := strconv.FormatUint(uint64(netAddress.Port), 10) - - return AddressKey(net.JoinHostPort(netAddress.IP.String(), port)) -} - -// GetAddress returns a single address that should be routable. It picks a -// random one from the possible addresses with preference given to ones that -// have not been used recently and should not pick 'close' addresses -// consecutively. -func (am *AddressManager) GetAddress() *KnownAddress { - // Protect concurrent access. - am.mutex.Lock() - defer am.mutex.Unlock() - - triedAddressBucketArray := am.triedAddressBucketArray(am.localSubnetworkID) - triedAddressCount := am.triedAddressCount(am.localSubnetworkID) - newAddressBucketArray := am.newAddressBucketArray(am.localSubnetworkID) - newAddressCount := am.newAddressCount(am.localSubnetworkID) - knownAddress := am.getAddress(triedAddressBucketArray, triedAddressCount, newAddressBucketArray, newAddressCount) - - return knownAddress - -} - -// getAddress returns a single address that should be routable. -// See GetAddress for further details. -func (am *AddressManager) getAddress(triedAddressBucketArray *triedAddressBucketArray, triedAddressCount int, - newAddressBucketArray *newAddressBucketArray, newAddressCount int) *KnownAddress { - - // Use a 50% chance for choosing between tried and new addresses. - var bucketArray addressBucketArray - if triedAddressCount > 0 && (newAddressCount == 0 || am.random.Intn(2) == 0) { - bucketArray = triedAddressBucketArray - } else if newAddressCount > 0 { - bucketArray = newAddressBucketArray - } else { - // There aren't any addresses in any of the buckets - return nil - } - - // Pick a random bucket - randomBucket := bucketArray.randomBucket(am.random) - - // Get the sum of all chances - totalChance := float64(0) - for _, knownAddress := range randomBucket { - totalChance += knownAddress.chance() - } - - // Pick a random address weighted by chance - randomValue := am.random.Float64() - accumulatedChance := float64(0) - for _, knownAddress := range randomBucket { - normalizedChance := knownAddress.chance() / totalChance - accumulatedChance += normalizedChance - if randomValue < accumulatedChance { - return knownAddress - } - } - - panic("randomValue is equal to or greater than 1, which cannot happen") -} - -type addressBucketArray interface { - name() string - randomBucket(random *rand.Rand) []*KnownAddress -} - -func (nb *newAddressBucketArray) randomBucket(random *rand.Rand) []*KnownAddress { - nonEmptyBuckets := make([]map[AddressKey]*KnownAddress, 0, NewBucketCount) - for _, bucket := range nb { - if len(bucket) > 0 { - nonEmptyBuckets = append(nonEmptyBuckets, bucket) - } - } - randomIndex := random.Intn(len(nonEmptyBuckets)) - randomBucket := nonEmptyBuckets[randomIndex] - - // Collect the known addresses into a slice - randomBucketSlice := make([]*KnownAddress, 0, len(randomBucket)) - for _, knownAddress := range randomBucket { - randomBucketSlice = append(randomBucketSlice, knownAddress) - } - return randomBucketSlice -} - -func (nb *newAddressBucketArray) name() string { - return "new" -} - -func (tb *triedAddressBucketArray) randomBucket(random *rand.Rand) []*KnownAddress { - nonEmptyBuckets := make([][]*KnownAddress, 0, TriedBucketCount) - for _, bucket := range tb { - if len(bucket) > 0 { - nonEmptyBuckets = append(nonEmptyBuckets, bucket) - } - } - randomIndex := random.Intn(len(nonEmptyBuckets)) - return nonEmptyBuckets[randomIndex] -} - -func (tb *triedAddressBucketArray) name() string { - return "tried" -} - -func (am *AddressManager) knownAddress(address *appmessage.NetAddress) *KnownAddress { - return am.addressIndex[NetAddressKey(address)] -} - -// Attempt increases the given address' attempt counter and updates -// the last attempt time. -func (am *AddressManager) Attempt(address *appmessage.NetAddress) { - am.mutex.Lock() - defer am.mutex.Unlock() - - // find address. - // Surely address will be in tried by now? - knownAddress := am.knownAddress(address) - if knownAddress == nil { - return - } - // set last tried time to now - knownAddress.attempts++ - knownAddress.lastAttempt = mstime.Now() -} - -// Connected Marks the given address as currently connected and working at the -// current time. The address must already be known to AddressManager else it will -// be ignored. -func (am *AddressManager) Connected(address *appmessage.NetAddress) { - am.mutex.Lock() - defer am.mutex.Unlock() - - knownAddress := am.knownAddress(address) - if knownAddress == nil { - return - } - - // Update the time as long as it has been 20 minutes since last we did - // so. - now := mstime.Now() - if now.After(knownAddress.netAddress.Timestamp.Add(time.Minute * 20)) { - // knownAddress.netAddress is immutable, so replace it. - netAddressCopy := *knownAddress.netAddress - netAddressCopy.Timestamp = mstime.Now() - knownAddress.netAddress = &netAddressCopy - } -} - -// Good marks the given address as good. To be called after a successful -// connection and version exchange. If the address is unknown to the address -// manager it will be ignored. -func (am *AddressManager) Good(address *appmessage.NetAddress, subnetworkID *subnetworkid.SubnetworkID) { - am.mutex.Lock() - defer am.mutex.Unlock() - - knownAddress := am.knownAddress(address) - if knownAddress == nil { - return - } - oldSubnetworkID := knownAddress.subnetworkID - - // knownAddress.Timestamp is not updated here to avoid leaking information - // about currently connected peers. - now := mstime.Now() - knownAddress.lastSuccess = now - knownAddress.lastAttempt = now - knownAddress.attempts = 0 - knownAddress.subnetworkID = subnetworkID - - addressKey := NetAddressKey(address) - triedAddressBucketIndex := am.triedAddressBucketIndex(knownAddress.netAddress) - - if knownAddress.tried { - // If this address was already tried, and subnetworkID didn't change - don't do anything - if subnetworkID.IsEqual(oldSubnetworkID) { - return - } - - // If this address was already tried, but subnetworkID was changed - - // update subnetworkID, than continue as though this is a new address - bucket := am.subnetworkTriedAddresBucketArrays[*oldSubnetworkID][triedAddressBucketIndex] - var toRemoveIndex int - toRemoveIndexFound := false - for i, knownAddress := range bucket { - if NetAddressKey(knownAddress.NetAddress()) == addressKey { - toRemoveIndex = i - toRemoveIndexFound = true - break - } - } - if toRemoveIndexFound { - am.subnetworkTriedAddresBucketArrays[*oldSubnetworkID][triedAddressBucketIndex] = - append(bucket[:toRemoveIndex], bucket[toRemoveIndex+1:]...) - } - } - - // Ok, need to move it to tried. - - // Remove from all new buckets. - // Record one of the buckets in question and call it the `oldBucketIndex' - var oldBucketIndex int - oldBucketIndexFound := false - if !knownAddress.tried { - newAddressBucketArray := am.newAddressBucketArray(oldSubnetworkID) - for i := range newAddressBucketArray { - // we check for existence so we can record the first one - if _, ok := newAddressBucketArray[i][addressKey]; ok { - if !oldBucketIndexFound { - oldBucketIndex = i - oldBucketIndexFound = true - } - - delete(newAddressBucketArray[i], addressKey) - knownAddress.referenceCount-- - } - } - - am.decrementNewAddressCount(oldSubnetworkID) - } - - // Room in this tried bucket? - triedAddressBucketArray := am.triedAddressBucketArray(knownAddress.subnetworkID) - triedAddressCount := am.triedAddressCount(knownAddress.subnetworkID) - if triedAddressCount == 0 || len(triedAddressBucketArray[triedAddressBucketIndex]) < triedBucketSize { - knownAddress.tried = true - am.updateAddrTried(triedAddressBucketIndex, knownAddress) - am.incrementTriedAddressCount(knownAddress.subnetworkID) - return - } - - // No room, we have to evict something else. - knownAddressToRemove, knownAddressToRemoveIndex := am.pickTried(knownAddress.subnetworkID, triedAddressBucketIndex) - - // First bucket index it would have been put in. - newAddressBucketIndex := am.newAddressBucketIndex(knownAddressToRemove.netAddress, knownAddressToRemove.sourceAddress) - - // If no room in the original bucket, we put it in a bucket we just - // freed up a space in. - newAddressBucketArray := am.newAddressBucketArray(knownAddress.subnetworkID) - if len(newAddressBucketArray[newAddressBucketIndex]) >= newBucketSize { - if !oldBucketIndexFound { - // If address was a tried bucket with updated subnetworkID - oldBucketIndex will be equal to -1. - // In that case - find some non-full bucket. - // If no such bucket exists - throw knownAddressToRemove away - for newBucket := range newAddressBucketArray { - if len(newAddressBucketArray[newBucket]) < newBucketSize { - break - } - } - } else { - newAddressBucketIndex = oldBucketIndex - } - } - - // Replace with knownAddress in the slice - knownAddress.tried = true - triedAddressBucketArray[triedAddressBucketIndex][knownAddressToRemoveIndex] = knownAddress - - knownAddressToRemove.tried = false - knownAddressToRemove.referenceCount++ - - // We don't touch a.subnetworkTriedAddressCounts here since the number of tried stays the same - // but we decremented new above, raise it again since we're putting - // something back. - am.incrementNewAddressCount(knownAddress.subnetworkID) - - knownAddressToRemoveKey := NetAddressKey(knownAddressToRemove.netAddress) - log.Tracef("Replacing %s with %s in tried", knownAddressToRemoveKey, addressKey) - - // We made sure there is space here just above. - newAddressBucketArray[newAddressBucketIndex][knownAddressToRemoveKey] = knownAddressToRemove -} - -func (am *AddressManager) newAddressBucketArray(subnetworkID *subnetworkid.SubnetworkID) *newAddressBucketArray { - if subnetworkID == nil { - return &am.fullNodeNewAddressBucketArray - } - return am.subnetworkNewAddressBucketArrays[*subnetworkID] -} - -func (am *AddressManager) triedAddressBucketArray(subnetworkID *subnetworkid.SubnetworkID) *triedAddressBucketArray { - if subnetworkID == nil { - return &am.fullNodeTriedAddressBucketArray - } - return am.subnetworkTriedAddresBucketArrays[*subnetworkID] -} - -func (am *AddressManager) incrementNewAddressCount(subnetworkID *subnetworkid.SubnetworkID) { - if subnetworkID == nil { - am.fullNodeNewAddressCount++ - return - } - am.subnetworkNewAddressCounts[*subnetworkID]++ -} - -func (am *AddressManager) decrementNewAddressCount(subnetworkID *subnetworkid.SubnetworkID) { - if subnetworkID == nil { - am.fullNodeNewAddressCount-- - return - } - am.subnetworkNewAddressCounts[*subnetworkID]-- -} - -func (am *AddressManager) triedAddressCount(subnetworkID *subnetworkid.SubnetworkID) int { - if subnetworkID == nil { - return am.fullNodeTriedAddressCount - } - return am.subnetworkTriedAddressCounts[*subnetworkID] -} - -func (am *AddressManager) newAddressCount(subnetworkID *subnetworkid.SubnetworkID) int { - if subnetworkID == nil { - return am.fullNodeNewAddressCount - } - return am.subnetworkNewAddressCounts[*subnetworkID] -} - -func (am *AddressManager) incrementTriedAddressCount(subnetworkID *subnetworkid.SubnetworkID) { - if subnetworkID == nil { - am.fullNodeTriedAddressCount++ - return - } - am.subnetworkTriedAddressCounts[*subnetworkID]++ -} - -// AddLocalAddress adds netAddress to the list of known local addresses to advertise -// with the given priority. -func (am *AddressManager) AddLocalAddress(netAddress *appmessage.NetAddress, priority AddressPriority) error { - if !am.IsRoutable(netAddress) { - return errors.Errorf("address %s is not routable", netAddress.IP) - } - - am.localAddressesLock.Lock() - defer am.localAddressesLock.Unlock() - - addressKey := NetAddressKey(netAddress) - address, ok := am.localAddresses[addressKey] - if !ok || address.score < priority { - if ok { - address.score = priority + 1 - } else { - am.localAddresses[addressKey] = &localAddress{ - netAddress: netAddress, - score: priority, - } - } - } - return nil -} - -// getReachabilityFrom returns the relative reachability of the provided local -// address to the provided remote address. -func (am *AddressManager) getReachabilityFrom(localAddress, remoteAddress *appmessage.NetAddress) int { - const ( - Unreachable = 0 - Default = iota - Teredo - Ipv6Weak - Ipv4 - Ipv6Strong - Private - ) - - if !am.IsRoutable(remoteAddress) { - return Unreachable - } - - if IsRFC4380(remoteAddress) { - if !am.IsRoutable(localAddress) { - return Default - } - - if IsRFC4380(localAddress) { - return Teredo - } - - if IsIPv4(localAddress) { - return Ipv4 - } - - return Ipv6Weak - } - - if IsIPv4(remoteAddress) { - if am.IsRoutable(localAddress) && IsIPv4(localAddress) { - return Ipv4 - } - return Unreachable - } - - /* ipv6 */ - var tunnelled bool - // Is our v6 is tunnelled? - if IsRFC3964(localAddress) || IsRFC6052(localAddress) || IsRFC6145(localAddress) { - tunnelled = true - } - - if !am.IsRoutable(localAddress) { - return Default - } - - if IsRFC4380(localAddress) { - return Teredo - } - - if IsIPv4(localAddress) { - return Ipv4 - } - - if tunnelled { - // only prioritise ipv6 if we aren't tunnelling it. - return Ipv6Weak - } - - return Ipv6Strong -} - -// GetBestLocalAddress returns the most appropriate local address to use +// BestLocalAddress returns the most appropriate local address to use // for the given remote address. -func (am *AddressManager) GetBestLocalAddress(remoteAddress *appmessage.NetAddress) *appmessage.NetAddress { - am.localAddressesLock.Lock() - defer am.localAddressesLock.Unlock() - - bestReach := 0 - var bestScore AddressPriority - var bestAddress *appmessage.NetAddress - for _, localAddress := range am.localAddresses { - reach := am.getReachabilityFrom(localAddress.netAddress, remoteAddress) - if reach > bestReach || - (reach == bestReach && localAddress.score > bestScore) { - bestReach = reach - bestScore = localAddress.score - bestAddress = localAddress.netAddress - } - } - if bestAddress != nil { - log.Debugf("Suggesting address %s:%d for %s:%d", bestAddress.IP, - bestAddress.Port, remoteAddress.IP, remoteAddress.Port) - } else { - log.Debugf("No worthy address for %s:%d", remoteAddress.IP, - remoteAddress.Port) - - // Send something unroutable if nothing suitable. - var ip net.IP - if !IsIPv4(remoteAddress) { - ip = net.IPv6zero - } else { - ip = net.IPv4zero - } - services := appmessage.SFNodeNetwork | appmessage.SFNodeBloom - bestAddress = appmessage.NewNetAddressIPPort(ip, 0, services) - } - - return bestAddress +func (am *AddressManager) BestLocalAddress(remoteAddress *appmessage.NetAddress) *appmessage.NetAddress { + return am.localAddresses.bestLocalAddress(remoteAddress) } // Ban marks the given address as banned func (am *AddressManager) Ban(address *appmessage.NetAddress) error { - return am.setBanned(address, true, mstime.Now()) -} + am.mutex.Lock() + defer am.mutex.Unlock() -// Unban marks the given address as not banned -func (am *AddressManager) Unban(address *appmessage.NetAddress) error { - return am.setBanned(address, false, mstime.Time{}) -} - -func (am *AddressManager) setBanned(address *appmessage.NetAddress, isBanned bool, bannedTime mstime.Time) error { - am.localAddressesLock.Lock() - defer am.localAddressesLock.Unlock() - - knownAddress := am.knownAddress(address) - if knownAddress == nil { + key := netAddressKey(address) + addressToBan, ok := am.addresses[key] + if !ok { return errors.Wrapf(ErrAddressNotFound, "address %s "+ "is not registered with the address manager", address.TCPAddress()) } - knownAddress.isBanned = isBanned - knownAddress.bannedTime = bannedTime + + delete(am.addresses, key) + am.bannedAddresses[key] = addressToBan + return nil + +} + +// Unban unmarks the given address as banned +func (am *AddressManager) Unban(address *appmessage.NetAddress) error { + am.mutex.Lock() + defer am.mutex.Unlock() + + key := netAddressKey(address) + bannedAddress, ok := am.bannedAddresses[key] + if !ok { + return errors.Wrapf(ErrAddressNotFound, "address %s "+ + "is not registered with the address manager as banned", address.TCPAddress()) + } + + delete(am.bannedAddresses, key) + am.addresses[key] = bannedAddress return nil } -// IsBanned returns whether the given address is banned +// IsBanned returns true if the given address is marked as banned func (am *AddressManager) IsBanned(address *appmessage.NetAddress) (bool, error) { - am.localAddressesLock.Lock() - defer am.localAddressesLock.Unlock() + am.mutex.Lock() + defer am.mutex.Unlock() - knownAddress := am.knownAddress(address) - if knownAddress == nil { - return false, errors.Wrapf(ErrAddressNotFound, "address %s "+ - "is not registered with the address manager", address.TCPAddress()) - } - return knownAddress.isBanned, nil -} - -// initListeners initializes the configured net listeners and adds any bound -// addresses to the address manager -func (am *AddressManager) initListeners() error { - if len(am.cfg.ExternalIPs) != 0 { - defaultPort, err := strconv.ParseUint(am.cfg.NetParams().DefaultPort, 10, 16) - if err != nil { - log.Errorf("Can not parse default port %s for active DAG: %s", - am.cfg.NetParams().DefaultPort, err) - return err - } - - for _, sip := range am.cfg.ExternalIPs { - eport := uint16(defaultPort) - host, portstr, err := net.SplitHostPort(sip) - if err != nil { - // no port, use default. - host = sip - } else { - port, err := strconv.ParseUint(portstr, 10, 16) - if err != nil { - log.Warnf("Can not parse port from %s for "+ - "externalip: %s", sip, err) - continue - } - eport = uint16(port) - } - na, err := am.HostToNetAddress(host, eport, appmessage.DefaultServices) - if err != nil { - log.Warnf("Not adding %s as externalip: %s", sip, err) - continue - } - - err = am.AddLocalAddress(na, ManualPrio) - if err != nil { - log.Warnf("Skipping specified external IP: %s", err) - } - } - } else { - // Listen for TCP connections at the configured addresses - netAddrs, err := parseListeners(am.cfg.Listeners) - if err != nil { - return err - } - - // Add bound addresses to address manager to be advertised to peers. - for _, addr := range netAddrs { - listener, err := net.Listen(addr.Network(), addr.String()) - if err != nil { - log.Warnf("Can't listen on %s: %s", addr, err) - continue - } - addr := listener.Addr().String() - err = listener.Close() - if err != nil { - return err - } - err = am.addLocalAddress(addr) - if err != nil { - log.Warnf("Skipping bound address %s: %s", addr, err) - } + key := netAddressKey(address) + if _, ok := am.bannedAddresses[key]; !ok { + if _, ok = am.addresses[key]; !ok { + return false, errors.Wrapf(ErrAddressNotFound, "address %s "+ + "is not registered with the address manager", address.TCPAddress()) } + return false, nil } - return nil + return true, nil + } - -// parseListeners determines whether each listen address is IPv4 and IPv6 and -// returns a slice of appropriate net.Addrs to listen on with TCP. It also -// properly detects addresses which apply to "all interfaces" and adds the -// address as both IPv4 and IPv6. -func parseListeners(addrs []string) ([]net.Addr, error) { - netAddrs := make([]net.Addr, 0, len(addrs)*2) - for _, addr := range addrs { - host, _, err := net.SplitHostPort(addr) - if err != nil { - // Shouldn't happen due to already being normalized. - return nil, err - } - - // Empty host or host of * on plan9 is both IPv4 and IPv6. - if host == "" || (host == "*" && runtime.GOOS == "plan9") { - netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) - netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) - continue - } - - // Strip IPv6 zone id if present since net.ParseIP does not - // handle it. - zoneIndex := strings.LastIndex(host, "%") - if zoneIndex > 0 { - host = host[:zoneIndex] - } - - // Parse the IP. - ip := net.ParseIP(host) - if ip == nil { - hostAddrs, err := net.LookupHost(host) - if err != nil { - return nil, err - } - ip = net.ParseIP(hostAddrs[0]) - if ip == nil { - return nil, errors.Errorf("Cannot resolve IP address for host '%s'", host) - } - } - - // To4 returns nil when the IP is not an IPv4 address, so use - // this determine the address type. - if ip.To4() == nil { - netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) - } else { - netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) - } - } - return netAddrs, nil -} - -// addLocalAddress adds an address that this node is listening on to the -// address manager so that it may be relayed to peers. -func (am *AddressManager) addLocalAddress(addr string) error { - host, portStr, err := net.SplitHostPort(addr) - if err != nil { - return err - } - port, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return err - } - - if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() { - // If bound to unspecified address, advertise all local interfaces - addrs, err := net.InterfaceAddrs() - if err != nil { - return err - } - - for _, addr := range addrs { - ifaceIP, _, err := net.ParseCIDR(addr.String()) - if err != nil { - continue - } - - // If bound to 0.0.0.0, do not add IPv6 interfaces and if bound to - // ::, do not add IPv4 interfaces. - if (ip.To4() == nil) != (ifaceIP.To4() == nil) { - continue - } - - netAddr := appmessage.NewNetAddressIPPort(ifaceIP, uint16(port), appmessage.DefaultServices) - am.AddLocalAddress(netAddr, BoundPrio) - } - } else { - netAddr, err := am.HostToNetAddress(host, uint16(port), appmessage.DefaultServices) - if err != nil { - return err - } - - am.AddLocalAddress(netAddr, BoundPrio) - } - - return nil -} - -// simpleAddr implements the net.Addr interface with two struct fields -type simpleAddr struct { - net, addr string -} - -// String returns the address. -// -// This is part of the net.Addr interface. -func (a simpleAddr) String() string { - return a.addr -} - -// Network returns the network. -// -// This is part of the net.Addr interface. -func (a simpleAddr) Network() string { - return a.net -} - -// Ensure simpleAddr implements the net.Addr interface. -var _ net.Addr = simpleAddr{} diff --git a/infrastructure/network/addressmanager/addressmanager_test.go b/infrastructure/network/addressmanager/addressmanager_test.go index 85030c12a..1a5564a5f 100644 --- a/infrastructure/network/addressmanager/addressmanager_test.go +++ b/infrastructure/network/addressmanager/addressmanager_test.go @@ -5,518 +5,27 @@ package addressmanager import ( - "fmt" - "github.com/kaspanet/kaspad/app/appmessage" - "io/ioutil" "net" - "reflect" "testing" - "time" + + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/infrastructure/config" - "github.com/kaspanet/kaspad/infrastructure/db/dbaccess" - "github.com/kaspanet/kaspad/util/mstime" - "github.com/kaspanet/kaspad/util/subnetworkid" - - "github.com/pkg/errors" ) -// naTest is used to describe a test to be performed against the NetAddressKey -// method. -type naTest struct { - in appmessage.NetAddress - want AddressKey -} - -// naTests houses all of the tests to be performed against the NetAddressKey -// method. -var naTests = make([]naTest, 0) - -// Put some IP in here for convenience. Points to google. -var someIP = "173.194.115.66" - -// addNaTests -func addNaTests() { - // IPv4 - // Localhost - addNaTest("127.0.0.1", 16111, "127.0.0.1:16111") - addNaTest("127.0.0.1", 16110, "127.0.0.1:16110") - - // Class A - addNaTest("1.0.0.1", 16111, "1.0.0.1:16111") - addNaTest("2.2.2.2", 16110, "2.2.2.2:16110") - addNaTest("27.253.252.251", 8335, "27.253.252.251:8335") - addNaTest("123.3.2.1", 8336, "123.3.2.1:8336") - - // Private Class A - addNaTest("10.0.0.1", 16111, "10.0.0.1:16111") - addNaTest("10.1.1.1", 16110, "10.1.1.1:16110") - addNaTest("10.2.2.2", 8335, "10.2.2.2:8335") - addNaTest("10.10.10.10", 8336, "10.10.10.10:8336") - - // Class B - addNaTest("128.0.0.1", 16111, "128.0.0.1:16111") - addNaTest("129.1.1.1", 16110, "129.1.1.1:16110") - addNaTest("180.2.2.2", 8335, "180.2.2.2:8335") - addNaTest("191.10.10.10", 8336, "191.10.10.10:8336") - - // Private Class B - addNaTest("172.16.0.1", 16111, "172.16.0.1:16111") - addNaTest("172.16.1.1", 16110, "172.16.1.1:16110") - addNaTest("172.16.2.2", 8335, "172.16.2.2:8335") - addNaTest("172.16.172.172", 8336, "172.16.172.172:8336") - - // Class C - addNaTest("193.0.0.1", 16111, "193.0.0.1:16111") - addNaTest("200.1.1.1", 16110, "200.1.1.1:16110") - addNaTest("205.2.2.2", 8335, "205.2.2.2:8335") - addNaTest("223.10.10.10", 8336, "223.10.10.10:8336") - - // Private Class C - addNaTest("192.168.0.1", 16111, "192.168.0.1:16111") - addNaTest("192.168.1.1", 16110, "192.168.1.1:16110") - addNaTest("192.168.2.2", 8335, "192.168.2.2:8335") - addNaTest("192.168.192.192", 8336, "192.168.192.192:8336") - - // IPv6 - // Localhost - addNaTest("::1", 16111, "[::1]:16111") - addNaTest("fe80::1", 16110, "[fe80::1]:16110") - - // Link-local - addNaTest("fe80::1:1", 16111, "[fe80::1:1]:16111") - addNaTest("fe91::2:2", 16110, "[fe91::2:2]:16110") - addNaTest("fea2::3:3", 8335, "[fea2::3:3]:8335") - addNaTest("feb3::4:4", 8336, "[feb3::4:4]:8336") - - // Site-local - addNaTest("fec0::1:1", 16111, "[fec0::1:1]:16111") - addNaTest("fed1::2:2", 16110, "[fed1::2:2]:16110") - addNaTest("fee2::3:3", 8335, "[fee2::3:3]:8335") - addNaTest("fef3::4:4", 8336, "[fef3::4:4]:8336") -} - -func addNaTest(ip string, port uint16, want AddressKey) { - nip := net.ParseIP(ip) - na := *appmessage.NewNetAddressIPPort(nip, port, appmessage.SFNodeNetwork) - test := naTest{na, want} - naTests = append(naTests, test) -} - -func lookupFuncForTest(host string) ([]net.IP, error) { - return nil, errors.New("not implemented") -} - -func newAddrManagerForTest(t *testing.T, testName string, - localSubnetworkID *subnetworkid.SubnetworkID) (addressManager *AddressManager, teardown func()) { - +func newAddrManagerForTest(t *testing.T, testName string) (addressManager *AddressManager, teardown func()) { cfg := config.DefaultConfig() - cfg.SubnetworkID = localSubnetworkID - dbPath, err := ioutil.TempDir("", testName) - if err != nil { - t.Fatalf("Error creating temporary directory: %s", err) - } - - databaseContext, err := dbaccess.New(dbPath) - if err != nil { - t.Fatalf("error creating db: %s", err) - } - - addressManager, err = New(cfg, databaseContext) + addressManager, err := New(NewConfig(cfg)) if err != nil { t.Fatalf("error creating address manager: %s", err) } return addressManager, func() { - err := databaseContext.Close() - if err != nil { - t.Fatalf("error closing the database: %s", err) - } } } -func TestStartStop(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestStartStop", nil) - defer teardown() - err := amgr.Start() - if err != nil { - t.Fatalf("Address Manager failed to start: %v", err) - } - err = amgr.Stop() - if err != nil { - t.Fatalf("Address Manager failed to stop: %v", err) - } -} - -func TestAddAddressByIP(t *testing.T) { - fmtErr := errors.Errorf("") - addrErr := &net.AddrError{} - var tests = []struct { - addrIP string - err error - }{ - { - someIP + ":16111", - nil, - }, - { - someIP, - addrErr, - }, - { - someIP[:12] + ":8333", - fmtErr, - }, - { - someIP + ":abcd", - fmtErr, - }, - } - - amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil) - defer teardown() - for i, test := range tests { - err := AddAddressByIP(amgr, test.addrIP, nil) - if test.err != nil && err == nil { - t.Errorf("TestAddAddressByIP test %d failed expected an error and got none", i) - continue - } - if test.err == nil && err != nil { - t.Errorf("TestAddAddressByIP test %d failed expected no error and got one", i) - continue - } - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("TestAddAddressByIP test %d failed got %v, want %v", i, - reflect.TypeOf(err), reflect.TypeOf(test.err)) - continue - } - } -} - -func TestAddLocalAddress(t *testing.T) { - var tests = []struct { - address appmessage.NetAddress - priority AddressPriority - valid bool - }{ - { - appmessage.NetAddress{IP: net.ParseIP("192.168.0.100")}, - InterfacePrio, - false, - }, - { - appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")}, - InterfacePrio, - true, - }, - { - appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")}, - BoundPrio, - true, - }, - { - appmessage.NetAddress{IP: net.ParseIP("::1")}, - InterfacePrio, - false, - }, - { - appmessage.NetAddress{IP: net.ParseIP("fe80::1")}, - InterfacePrio, - false, - }, - { - appmessage.NetAddress{IP: net.ParseIP("2620:100::1")}, - InterfacePrio, - true, - }, - } - amgr, teardown := newAddrManagerForTest(t, "TestAddLocalAddress", nil) - defer teardown() - for x, test := range tests { - result := amgr.AddLocalAddress(&test.address, test.priority) - if result == nil && !test.valid { - t.Errorf("TestAddLocalAddress test #%d failed: %s should have "+ - "been accepted", x, test.address.IP) - continue - } - if result != nil && test.valid { - t.Errorf("TestAddLocalAddress test #%d failed: %s should not have "+ - "been accepted", x, test.address.IP) - continue - } - } -} - -func TestAttempt(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestAttempt", nil) - defer teardown() - - // Add a new address and get it - err := AddAddressByIP(amgr, someIP+":8333", nil) - if err != nil { - t.Fatalf("Adding address failed: %v", err) - } - ka := amgr.GetAddress() - - if !ka.LastAttempt().IsZero() { - t.Errorf("Address should not have attempts, but does") - } - - na := ka.NetAddress() - amgr.Attempt(na) - - if ka.LastAttempt().IsZero() { - t.Errorf("Address should have an attempt, but does not") - } -} - -func TestConnected(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestConnected", nil) - defer teardown() - - // Add a new address and get it - err := AddAddressByIP(amgr, someIP+":8333", nil) - if err != nil { - t.Fatalf("Adding address failed: %v", err) - } - ka := amgr.GetAddress() - na := ka.NetAddress() - // make it an hour ago - na.Timestamp = mstime.Now().Add(time.Hour * -1) - - amgr.Connected(na) - - if !ka.NetAddress().Timestamp.After(na.Timestamp) { - t.Errorf("Address should have a new timestamp, but does not") - } -} - -func TestNeedMoreAddresses(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestNeedMoreAddresses", nil) - defer teardown() - addrsToAdd := 1500 - b := amgr.NeedMoreAddresses() - if !b { - t.Errorf("Expected that we need more addresses") - } - addrs := make([]*appmessage.NetAddress, addrsToAdd) - - var err error - for i := 0; i < addrsToAdd; i++ { - s := AddressKey(fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60)) - addrs[i], err = amgr.DeserializeNetAddress(s) - if err != nil { - t.Errorf("Failed to turn %s into an address: %v", s, err) - } - } - - srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0) - - amgr.AddAddresses(addrs, srcAddr, nil) - numAddrs := amgr.TotalNumAddresses() - if numAddrs > addrsToAdd { - t.Errorf("Number of addresses is too many %d vs %d", numAddrs, addrsToAdd) - } - - b = amgr.NeedMoreAddresses() - if b { - t.Errorf("Expected that we don't need more addresses") - } -} - -func TestGood(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestGood", nil) - defer teardown() - addrsToAdd := 64 * 64 - addrs := make([]*appmessage.NetAddress, addrsToAdd) - subnetworkCount := 32 - subnetworkIDs := make([]*subnetworkid.SubnetworkID, subnetworkCount) - - var err error - for i := 0; i < addrsToAdd; i++ { - s := AddressKey(fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60)) - addrs[i], err = amgr.DeserializeNetAddress(s) - if err != nil { - t.Errorf("Failed to turn %s into an address: %v", s, err) - } - } - - for i := 0; i < subnetworkCount; i++ { - subnetworkIDs[i] = &subnetworkid.SubnetworkID{0xff - byte(i)} - } - - srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0) - - amgr.AddAddresses(addrs, srcAddr, nil) - for i, addr := range addrs { - amgr.Good(addr, subnetworkIDs[i%subnetworkCount]) - } - - numAddrs := amgr.TotalNumAddresses() - if numAddrs >= addrsToAdd { - t.Errorf("Number of addresses is too many: %d vs %d", numAddrs, addrsToAdd) - } - - numCache := len(amgr.AddressCache(true, nil)) - if numCache == 0 || numCache >= numAddrs/4 { - t.Errorf("Number of addresses in cache: got %d, want positive and less than %d", - numCache, numAddrs/4) - } - - for i := 0; i < subnetworkCount; i++ { - numCache = len(amgr.AddressCache(false, subnetworkIDs[i])) - if numCache == 0 || numCache >= numAddrs/subnetworkCount { - t.Errorf("Number of addresses in subnetwork cache: got %d, want positive and less than %d", - numCache, numAddrs/4/subnetworkCount) - } - } -} - -func TestGoodChangeSubnetworkID(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestGoodChangeSubnetworkID", nil) - defer teardown() - addr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0) - addrKey := NetAddressKey(addr) - srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0) - - oldSubnetwork := subnetworkid.SubnetworkIDNative - amgr.AddAddress(addr, srcAddr, oldSubnetwork) - amgr.Good(addr, oldSubnetwork) - - // make sure address was saved to addressIndex under oldSubnetwork - ka := amgr.knownAddress(addr) - if ka == nil { - t.Fatalf("Address was not found after first time .Good called") - } - if !ka.SubnetworkID().IsEqual(oldSubnetwork) { - t.Fatalf("Address index did not point to oldSubnetwork") - } - - // make sure address was added to correct bucket under oldSubnetwork - bucket := amgr.subnetworkTriedAddresBucketArrays[*oldSubnetwork][amgr.triedAddressBucketIndex(addr)] - wasFound := false - for _, ka := range bucket { - if NetAddressKey(ka.NetAddress()) == addrKey { - wasFound = true - } - } - if !wasFound { - t.Fatalf("Address was not found in the correct bucket in oldSubnetwork") - } - - // now call .Good again with a different subnetwork - newSubnetwork := subnetworkid.SubnetworkIDRegistry - amgr.Good(addr, newSubnetwork) - - // make sure address was updated in addressIndex under newSubnetwork - ka = amgr.knownAddress(addr) - if ka == nil { - t.Fatalf("Address was not found after second time .Good called") - } - if !ka.SubnetworkID().IsEqual(newSubnetwork) { - t.Fatalf("Address index did not point to newSubnetwork") - } - - // make sure address was removed from bucket under oldSubnetwork - bucket = amgr.subnetworkTriedAddresBucketArrays[*oldSubnetwork][amgr.triedAddressBucketIndex(addr)] - wasFound = false - for _, ka := range bucket { - if NetAddressKey(ka.NetAddress()) == addrKey { - wasFound = true - } - } - if wasFound { - t.Fatalf("Address was not removed from bucket in oldSubnetwork") - } - - // make sure address was added to correct bucket under newSubnetwork - bucket = amgr.subnetworkTriedAddresBucketArrays[*newSubnetwork][amgr.triedAddressBucketIndex(addr)] - wasFound = false - for _, ka := range bucket { - if NetAddressKey(ka.NetAddress()) == addrKey { - wasFound = true - } - } - if !wasFound { - t.Fatalf("Address was not found in the correct bucket in newSubnetwork") - } -} - -func TestGetAddress(t *testing.T) { - localSubnetworkID := &subnetworkid.SubnetworkID{0xff} - amgr, teardown := newAddrManagerForTest(t, "TestGetAddress", localSubnetworkID) - defer teardown() - - // Get an address from an empty set (should error) - if rv := amgr.GetAddress(); rv != nil { - t.Errorf("GetAddress failed: got: %v want: %v\n", rv, nil) - } - - // Add a new address and get it - err := AddAddressByIP(amgr, someIP+":8332", localSubnetworkID) - if err != nil { - t.Fatalf("Adding address failed: %v", err) - } - ka := amgr.GetAddress() - if ka == nil { - t.Fatalf("Did not get an address where there is one in the pool") - } - amgr.Attempt(ka.NetAddress()) - - // Checks that we don't get it if we find that it has other subnetwork ID than expected. - actualSubnetworkID := &subnetworkid.SubnetworkID{0xfe} - amgr.Good(ka.NetAddress(), actualSubnetworkID) - ka = amgr.GetAddress() - if ka != nil { - t.Errorf("Didn't expect to get an address because there shouldn't be any address from subnetwork ID %s or nil", localSubnetworkID) - } - - // Checks that the total number of addresses incremented although the new address is not full node or a partial node of the same subnetwork as the local node. - numAddrs := amgr.TotalNumAddresses() - if numAddrs != 1 { - t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1) - } - - // Now we repeat the same process, but now the address has the expected subnetwork ID. - - // Add a new address and get it - err = AddAddressByIP(amgr, someIP+":8333", localSubnetworkID) - if err != nil { - t.Fatalf("Adding address failed: %v", err) - } - ka = amgr.GetAddress() - if ka == nil { - t.Fatalf("Did not get an address where there is one in the pool") - } - if ka.NetAddress().IP.String() != someIP { - t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP) - } - if !ka.SubnetworkID().IsEqual(localSubnetworkID) { - t.Errorf("Wrong Subnetwork ID: got %v, want %v", *ka.SubnetworkID(), localSubnetworkID) - } - amgr.Attempt(ka.NetAddress()) - - // Mark this as a good address and get it - amgr.Good(ka.NetAddress(), localSubnetworkID) - ka = amgr.GetAddress() - if ka == nil { - t.Fatalf("Did not get an address where there is one in the pool") - } - if ka.NetAddress().IP.String() != someIP { - t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP) - } - if *ka.SubnetworkID() != *localSubnetworkID { - t.Errorf("Wrong Subnetwork ID: got %v, want %v", ka.SubnetworkID(), localSubnetworkID) - } - - numAddrs = amgr.TotalNumAddresses() - if numAddrs != 2 { - t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1) - } -} - -func TestGetBestLocalAddress(t *testing.T) { +func TestBestLocalAddress(t *testing.T) { localAddrs := []appmessage.NetAddress{ {IP: net.ParseIP("192.168.0.100")}, {IP: net.ParseIP("::1")}, @@ -557,12 +66,12 @@ func TestGetBestLocalAddress(t *testing.T) { }, } - amgr, teardown := newAddrManagerForTest(t, "TestGetBestLocalAddress", nil) + amgr, teardown := newAddrManagerForTest(t, "TestGetBestLocalAddress") defer teardown() // Test against default when there's no address for x, test := range tests { - got := amgr.GetBestLocalAddress(&test.remoteAddr) + got := amgr.BestLocalAddress(&test.remoteAddr) if !test.want0.IP.Equal(got.IP) { t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s", x, test.remoteAddr.IP, test.want1.IP, got.IP) @@ -571,12 +80,12 @@ func TestGetBestLocalAddress(t *testing.T) { } for _, localAddr := range localAddrs { - amgr.AddLocalAddress(&localAddr, InterfacePrio) + amgr.localAddresses.addLocalNetAddress(&localAddr, InterfacePrio) } // Test against want1 for x, test := range tests { - got := amgr.GetBestLocalAddress(&test.remoteAddr) + got := amgr.BestLocalAddress(&test.remoteAddr) if !test.want1.IP.Equal(got.IP) { t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s", x, test.remoteAddr.IP, test.want1.IP, got.IP) @@ -586,42 +95,15 @@ func TestGetBestLocalAddress(t *testing.T) { // Add a public IP to the list of local addresses. localAddr := appmessage.NetAddress{IP: net.ParseIP("204.124.8.100")} - amgr.AddLocalAddress(&localAddr, InterfacePrio) + amgr.localAddresses.addLocalNetAddress(&localAddr, InterfacePrio) // Test against want2 for x, test := range tests { - got := amgr.GetBestLocalAddress(&test.remoteAddr) + got := amgr.BestLocalAddress(&test.remoteAddr) if !test.want2.IP.Equal(got.IP) { t.Errorf("TestGetBestLocalAddress test2 #%d failed for remote address %s: want %s got %s", x, test.remoteAddr.IP, test.want2.IP, got.IP) continue } } - /* - // Add a Tor generated IP address - localAddr = appmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")} - amgr.AddLocalAddress(&localAddr, ManualPrio) - // Test against want3 - for x, test := range tests { - got := amgr.GetBestLocalAddress(&test.remoteAddr) - if !test.want3.IP.Equal(got.IP) { - t.Errorf("TestGetBestLocalAddress test3 #%d failed for remote address %s: want %s got %s", - x, test.remoteAddr.IP, test.want3.IP, got.IP) - continue - } - } - */ -} - -func TestNetAddressKey(t *testing.T) { - addNaTests() - - t.Logf("Running %d tests", len(naTests)) - for i, test := range naTests { - key := NetAddressKey(&test.in) - if key != test.want { - t.Errorf("NetAddressKey #%d\n got: %s want: %s", i, key, test.want) - continue - } - } } diff --git a/infrastructure/network/addressmanager/addressrandomize.go b/infrastructure/network/addressmanager/addressrandomize.go new file mode 100644 index 000000000..35c296293 --- /dev/null +++ b/infrastructure/network/addressmanager/addressrandomize.go @@ -0,0 +1,43 @@ +package addressmanager + +import ( + "math/rand" + "time" + + "github.com/kaspanet/kaspad/app/appmessage" +) + +// AddressRandomize implement AddressRandomizer interface +type AddressRandomize struct { + random *rand.Rand +} + +// NewAddressRandomize returns a new RandomizeAddress. +func NewAddressRandomize() *AddressRandomize { + return &AddressRandomize{ + random: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +// RandomAddress returns a random address from input list +func (amc *AddressRandomize) RandomAddress(addresses []*appmessage.NetAddress) *appmessage.NetAddress { + if len(addresses) > 0 { + randomIndex := rand.Intn(len(addresses)) + return addresses[randomIndex] + } + + return nil +} + +// RandomAddresses returns count addresses at random from input list +func (amc *AddressRandomize) RandomAddresses(addresses []*appmessage.NetAddress, count int) []*appmessage.NetAddress { + result := make([]*appmessage.NetAddress, 0, count) + if len(addresses) > 0 { + randomIndexes := rand.Perm(len(addresses)) + for i := 0; i < count; i++ { + result = append(result, addresses[randomIndexes[i]]) + } + } + + return result +} diff --git a/infrastructure/network/addressmanager/config.go b/infrastructure/network/addressmanager/config.go new file mode 100644 index 000000000..3e707044c --- /dev/null +++ b/infrastructure/network/addressmanager/config.go @@ -0,0 +1,27 @@ +package addressmanager + +import ( + "net" + + "github.com/kaspanet/kaspad/infrastructure/config" +) + +// Config is a descriptor which specifies the AddressManager instance configuration. +type Config struct { + AcceptUnroutable bool + DefaultPort string + ExternalIPs []string + Listeners []string + Lookup func(string) ([]net.IP, error) +} + +// NewConfig returns a new address manager Config. +func NewConfig(cfg *config.Config) *Config { + return &Config{ + AcceptUnroutable: cfg.NetParams().AcceptUnroutable, + DefaultPort: cfg.NetParams().DefaultPort, + ExternalIPs: cfg.ExternalIPs, + Listeners: cfg.Listeners, + Lookup: cfg.Lookup, + } +} diff --git a/infrastructure/network/addressmanager/internal_test.go b/infrastructure/network/addressmanager/internal_test.go deleted file mode 100644 index 51500f26f..000000000 --- a/infrastructure/network/addressmanager/internal_test.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package addressmanager - -import ( - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/util/mstime" -) - -func TstKnownAddressIsBad(ka *KnownAddress) bool { - return ka.isBad() -} - -func TstKnownAddressChance(ka *KnownAddress) float64 { - return ka.chance() -} - -func TstNewKnownAddress(na *appmessage.NetAddress, attempts int, - lastattempt, lastsuccess mstime.Time, tried bool, refs int) *KnownAddress { - return &KnownAddress{netAddress: na, attempts: attempts, lastAttempt: lastattempt, - lastSuccess: lastsuccess, tried: tried, referenceCount: refs} -} diff --git a/infrastructure/network/addressmanager/knownaddress.go b/infrastructure/network/addressmanager/knownaddress.go deleted file mode 100644 index 9e83e2801..000000000 --- a/infrastructure/network/addressmanager/knownaddress.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2013-2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package addressmanager - -import ( - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/util/mstime" - "time" - - "github.com/kaspanet/kaspad/util/subnetworkid" -) - -// KnownAddress tracks information about a known network address that is used -// to determine how viable an address is. -type KnownAddress struct { - netAddress *appmessage.NetAddress - sourceAddress *appmessage.NetAddress - attempts int - lastAttempt mstime.Time - lastSuccess mstime.Time - tried bool - referenceCount int // reference count of new buckets - subnetworkID *subnetworkid.SubnetworkID - isBanned bool - bannedTime mstime.Time -} - -// NetAddress returns the underlying appmessage.NetAddress associated with the -// known address. -func (ka *KnownAddress) NetAddress() *appmessage.NetAddress { - return ka.netAddress -} - -// SubnetworkID returns the subnetwork ID of the known address. -func (ka *KnownAddress) SubnetworkID() *subnetworkid.SubnetworkID { - return ka.subnetworkID -} - -// LastAttempt returns the last time the known address was attempted. -func (ka *KnownAddress) LastAttempt() mstime.Time { - return ka.lastAttempt -} - -// chance returns the selection probability for a known address. The priority -// depends upon how recently the address has been seen, how recently it was last -// attempted and how often attempts to connect to it have failed. -func (ka *KnownAddress) chance() float64 { - now := mstime.Now() - lastAttempt := now.Sub(ka.lastAttempt) - - if lastAttempt < 0 { - lastAttempt = 0 - } - - c := 1.0 - - // Very recent attempts are less likely to be retried. - if lastAttempt < 10*time.Minute { - c *= 0.01 - } - - // Failed attempts deprioritise. - for i := ka.attempts; i > 0; i-- { - c /= 1.5 - } - - return c -} - -// isBad returns true if the address in question has not been tried in the last -// minute and meets one of the following criteria: -// 1) It claims to be from the future -// 2) It hasn't been seen in over a month -// 3) It has failed at least three times and never succeeded -// 4) It has failed ten times in the last week -// All addresses that meet these criteria are assumed to be worthless and not -// worth keeping hold of. -func (ka *KnownAddress) isBad() bool { - if ka.lastAttempt.After(mstime.Now().Add(-1 * time.Minute)) { - return false - } - - // From the future? - if ka.netAddress.Timestamp.After(mstime.Now().Add(10 * time.Minute)) { - return true - } - - // Over a month old? - if ka.netAddress.Timestamp.Before(mstime.Now().Add(-1 * numMissingDays * time.Hour * 24)) { - return true - } - - // Never succeeded? - if ka.lastSuccess.IsZero() && ka.attempts >= numRetries { - return true - } - - // Hasn't succeeded in too long? - if !ka.lastSuccess.After(mstime.Now().Add(-1*minBadDays*time.Hour*24)) && - ka.attempts >= maxFailures { - return true - } - - return false -} diff --git a/infrastructure/network/addressmanager/knownaddress_test.go b/infrastructure/network/addressmanager/knownaddress_test.go deleted file mode 100644 index 5a439d5a4..000000000 --- a/infrastructure/network/addressmanager/knownaddress_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package addressmanager_test - -import ( - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/util/mstime" - "math" - "testing" - "time" - - "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" -) - -func TestChance(t *testing.T) { - now := mstime.Now() - var tests = []struct { - addr *addressmanager.KnownAddress - expected float64 - }{ - { - //Test normal case - addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)}, - 0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0), - 1.0, - }, { - //Test case in which lastseen < 0 - addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(20 * time.Second)}, - 0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0), - 1.0, - }, { - //Test case in which lastAttempt < 0 - addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)}, - 0, mstime.Now().Add(30*time.Minute), mstime.Now(), false, 0), - 1.0 * .01, - }, { - //Test case in which lastAttempt < ten minutes - addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)}, - 0, mstime.Now().Add(-5*time.Minute), mstime.Now(), false, 0), - 1.0 * .01, - }, { - //Test case with several failed attempts. - addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)}, - 2, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0), - 1 / 1.5 / 1.5, - }, - } - - err := .0001 - for i, test := range tests { - chance := addressmanager.TstKnownAddressChance(test.addr) - if math.Abs(test.expected-chance) >= err { - t.Errorf("case %d: got %f, expected %f", i, chance, test.expected) - } - } -} - -func TestIsBad(t *testing.T) { - now := mstime.Now() - future := now.Add(35 * time.Minute) - monthOld := now.Add(-43 * time.Hour * 24) - secondsOld := now.Add(-2 * time.Second) - minutesOld := now.Add(-27 * time.Minute) - hoursOld := now.Add(-5 * time.Hour) - zeroTime := mstime.Time{} - - futureNa := &appmessage.NetAddress{Timestamp: future} - minutesOldNa := &appmessage.NetAddress{Timestamp: minutesOld} - monthOldNa := &appmessage.NetAddress{Timestamp: monthOld} - currentNa := &appmessage.NetAddress{Timestamp: secondsOld} - - //Test addresses that have been tried in the last minute. - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) { - t.Errorf("test case 1: addresses that have been tried in the last minute are not bad.") - } - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(monthOldNa, 3, secondsOld, zeroTime, false, 0)) { - t.Errorf("test case 2: addresses that have been tried in the last minute are not bad.") - } - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 3, secondsOld, zeroTime, false, 0)) { - t.Errorf("test case 3: addresses that have been tried in the last minute are not bad.") - } - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 3, secondsOld, monthOld, true, 0)) { - t.Errorf("test case 4: addresses that have been tried in the last minute are not bad.") - } - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 2, secondsOld, secondsOld, true, 0)) { - t.Errorf("test case 5: addresses that have been tried in the last minute are not bad.") - } - - //Test address that claims to be from the future. - if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(futureNa, 0, minutesOld, hoursOld, true, 0)) { - t.Errorf("test case 6: addresses that claim to be from the future are bad.") - } - - //Test address that has not been seen in over a month. - if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(monthOldNa, 0, minutesOld, hoursOld, true, 0)) { - t.Errorf("test case 7: addresses more than a month old are bad.") - } - - //It has failed at least three times and never succeeded. - if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 3, minutesOld, zeroTime, true, 0)) { - t.Errorf("test case 8: addresses that have never succeeded are bad.") - } - - //It has failed ten times in the last week - if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 10, minutesOld, monthOld, true, 0)) { - t.Errorf("test case 9: addresses that have not succeeded in too long are bad.") - } - - //Test an address that should work. - if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 2, minutesOld, hoursOld, true, 0)) { - t.Errorf("test case 10: This should be a valid address.") - } -} diff --git a/infrastructure/network/addressmanager/localaddressmanager.go b/infrastructure/network/addressmanager/localaddressmanager.go new file mode 100644 index 000000000..d9274a432 --- /dev/null +++ b/infrastructure/network/addressmanager/localaddressmanager.go @@ -0,0 +1,400 @@ +package addressmanager + +import ( + "net" + "runtime" + "strconv" + "strings" + "sync" + + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/pkg/errors" +) + +// AddressPriority type is used to describe the hierarchy of local address +// discovery methods. +type AddressPriority int + +const ( + // InterfacePrio signifies the address is on a local interface + InterfacePrio AddressPriority = iota + + // BoundPrio signifies the address has been explicitly bounded to. + BoundPrio + + // UpnpPrio signifies the address was obtained from UPnP. + UpnpPrio + + // HTTPPrio signifies the address was obtained from an external HTTP service. + HTTPPrio + + // ManualPrio signifies the address was provided by --externalip. + ManualPrio +) + +type localAddress struct { + netAddress *appmessage.NetAddress + score AddressPriority +} + +type localAddressManager struct { + localAddresses map[AddressKey]*localAddress + lookupFunc func(string) ([]net.IP, error) + cfg *Config + mutex sync.Mutex +} + +func newLocalAddressManager(cfg *Config) (*localAddressManager, error) { + localAddressManager := localAddressManager{ + localAddresses: map[AddressKey]*localAddress{}, + cfg: cfg, + lookupFunc: cfg.Lookup, + } + + err := localAddressManager.initListeners() + if err != nil { + return nil, err + } + + return &localAddressManager, nil +} + +// addLocalNetAddress adds netAddress to the list of known local addresses to advertise +// with the given priority. +func (lam *localAddressManager) addLocalNetAddress(netAddress *appmessage.NetAddress, priority AddressPriority) error { + if !IsRoutable(netAddress, lam.cfg.AcceptUnroutable) { + return errors.Errorf("address %s is not routable", netAddress.IP) + } + + lam.mutex.Lock() + defer lam.mutex.Unlock() + + addressKey := netAddressKey(netAddress) + address, ok := lam.localAddresses[addressKey] + if !ok || address.score < priority { + if ok { + address.score = priority + 1 + } else { + lam.localAddresses[addressKey] = &localAddress{ + netAddress: netAddress, + score: priority, + } + } + } + return nil +} + +// bestLocalAddress returns the most appropriate local address to use +// for the given remote address. +func (lam *localAddressManager) bestLocalAddress(remoteAddress *appmessage.NetAddress) *appmessage.NetAddress { + lam.mutex.Lock() + defer lam.mutex.Unlock() + + bestReach := 0 + var bestScore AddressPriority + var bestAddress *appmessage.NetAddress + for _, localAddress := range lam.localAddresses { + reach := reachabilityFrom(localAddress.netAddress, remoteAddress, lam.cfg.AcceptUnroutable) + if reach > bestReach || + (reach == bestReach && localAddress.score > bestScore) { + bestReach = reach + bestScore = localAddress.score + bestAddress = localAddress.netAddress + } + } + + if bestAddress == nil { + // Send something unroutable if nothing suitable. + var ip net.IP + if !IsIPv4(remoteAddress) { + ip = net.IPv6zero + } else { + ip = net.IPv4zero + } + services := appmessage.SFNodeNetwork | appmessage.SFNodeBloom + bestAddress = appmessage.NewNetAddressIPPort(ip, 0, services) + } + + return bestAddress +} + +// addLocalAddress adds an address that this node is listening on to the +// address manager so that it may be relayed to peers. +func (lam *localAddressManager) addLocalAddress(addr string) error { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return err + } + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return err + } + + if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() { + // If bound to unspecified address, advertise all local interfaces + addrs, err := net.InterfaceAddrs() + if err != nil { + return err + } + + for _, addr := range addrs { + ifaceIP, _, err := net.ParseCIDR(addr.String()) + if err != nil { + continue + } + + // If bound to 0.0.0.0, do not add IPv6 interfaces and if bound to + // ::, do not add IPv4 interfaces. + if (ip.To4() == nil) != (ifaceIP.To4() == nil) { + continue + } + + netAddr := appmessage.NewNetAddressIPPort(ifaceIP, uint16(port), appmessage.DefaultServices) + lam.addLocalNetAddress(netAddr, BoundPrio) + } + } else { + netAddr, err := lam.hostToNetAddress(host, uint16(port), appmessage.DefaultServices) + if err != nil { + return err + } + + lam.addLocalNetAddress(netAddr, BoundPrio) + } + + return nil +} + +// initListeners initializes the configured net listeners and adds any bound +// addresses to the address manager +func (lam *localAddressManager) initListeners() error { + if len(lam.cfg.ExternalIPs) != 0 { + defaultPort, err := strconv.ParseUint(lam.cfg.DefaultPort, 10, 16) + if err != nil { + log.Errorf("Can not parse default port %s for active DAG: %s", + lam.cfg.DefaultPort, err) + return err + } + + for _, sip := range lam.cfg.ExternalIPs { + eport := uint16(defaultPort) + host, portstr, err := net.SplitHostPort(sip) + if err != nil { + // no port, use default. + host = sip + } else { + port, err := strconv.ParseUint(portstr, 10, 16) + if err != nil { + log.Warnf("Can not parse port from %s for "+ + "externalip: %s", sip, err) + continue + } + eport = uint16(port) + } + na, err := lam.hostToNetAddress(host, eport, appmessage.DefaultServices) + if err != nil { + log.Warnf("Not adding %s as externalip: %s", sip, err) + continue + } + + err = lam.addLocalNetAddress(na, ManualPrio) + if err != nil { + log.Warnf("Skipping specified external IP: %s", err) + } + } + } else { + // Listen for TCP connections at the configured addresses + netAddrs, err := parseListeners(lam.cfg.Listeners) + if err != nil { + return err + } + + // Add bound addresses to address manager to be advertised to peers. + for _, addr := range netAddrs { + listener, err := net.Listen(addr.Network(), addr.String()) + if err != nil { + log.Warnf("Can't listen on %s: %s", addr, err) + continue + } + addr := listener.Addr().String() + err = listener.Close() + if err != nil { + return err + } + err = lam.addLocalAddress(addr) + if err != nil { + log.Warnf("Skipping bound address %s: %s", addr, err) + } + } + } + + return nil +} + +// hostToNetAddress returns a netaddress given a host address. If +// the host is not an IP address it will be resolved. +func (lam *localAddressManager) hostToNetAddress(host string, port uint16, services appmessage.ServiceFlag) (*appmessage.NetAddress, error) { + ip := net.ParseIP(host) + if ip == nil { + ips, err := lam.lookupFunc(host) + if err != nil { + return nil, err + } + if len(ips) == 0 { + return nil, errors.Errorf("no addresses found for %s", host) + } + ip = ips[0] + } + + return appmessage.NewNetAddressIPPort(ip, port, services), nil +} + +// parseListeners determines whether each listen address is IPv4 and IPv6 and +// returns a slice of appropriate net.Addrs to listen on with TCP. It also +// properly detects addresses which apply to "all interfaces" and adds the +// address as both IPv4 and IPv6. +func parseListeners(addrs []string) ([]net.Addr, error) { + netAddrs := make([]net.Addr, 0, len(addrs)*2) + for _, addr := range addrs { + host, _, err := net.SplitHostPort(addr) + if err != nil { + // Shouldn't happen due to already being normalized. + return nil, err + } + + // Empty host or host of * on plan9 is both IPv4 and IPv6. + if host == "" || (host == "*" && runtime.GOOS == "plan9") { + netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) + netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) + continue + } + + // Strip IPv6 zone id if present since net.ParseIP does not + // handle it. + zoneIndex := strings.LastIndex(host, "%") + if zoneIndex > 0 { + host = host[:zoneIndex] + } + + // Parse the IP. + ip := net.ParseIP(host) + if ip == nil { + hostAddrs, err := net.LookupHost(host) + if err != nil { + return nil, err + } + ip = net.ParseIP(hostAddrs[0]) + if ip == nil { + return nil, errors.Errorf("Cannot resolve IP address for host '%s'", host) + } + } + + // To4 returns nil when the IP is not an IPv4 address, so use + // this determine the address type. + if ip.To4() == nil { + netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr}) + } else { + netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr}) + } + } + return netAddrs, nil +} + +// reachabilityFrom returns the relative reachability of the provided local +// address to the provided remote address. +func reachabilityFrom(localAddress, remoteAddress *appmessage.NetAddress, acceptUnroutable bool) int { + const ( + Unreachable = 0 + Default = iota + Teredo + Ipv6Weak + Ipv4 + Ipv6Strong + Private + ) + + IsRoutable := func(na *appmessage.NetAddress) bool { + if acceptUnroutable { + return !IsLocal(na) + } + + return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) || + IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) || + IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) || + IsLocal(na) || (IsRFC4193(na))) + } + + if !IsRoutable(remoteAddress) { + return Unreachable + } + + if IsRFC4380(remoteAddress) { + if !IsRoutable(localAddress) { + return Default + } + + if IsRFC4380(localAddress) { + return Teredo + } + + if IsIPv4(localAddress) { + return Ipv4 + } + + return Ipv6Weak + } + + if IsIPv4(remoteAddress) { + if IsRoutable(localAddress) && IsIPv4(localAddress) { + return Ipv4 + } + return Unreachable + } + + /* ipv6 */ + var tunnelled bool + // Is our v6 is tunnelled? + if IsRFC3964(localAddress) || IsRFC6052(localAddress) || IsRFC6145(localAddress) { + tunnelled = true + } + + if !IsRoutable(localAddress) { + return Default + } + + if IsRFC4380(localAddress) { + return Teredo + } + + if IsIPv4(localAddress) { + return Ipv4 + } + + if tunnelled { + // only prioritise ipv6 if we aren't tunnelling it. + return Ipv6Weak + } + + return Ipv6Strong +} + +// simpleAddr implements the net.Addr interface with two struct fields +type simpleAddr struct { + net, addr string +} + +// String returns the address. +// +// This is part of the net.Addr interface. +func (a simpleAddr) String() string { + return a.addr +} + +// Network returns the network. +// +// This is part of the net.Addr interface. +func (a simpleAddr) Network() string { + return a.net +} + +// Ensure simpleAddr implements the net.Addr interface. +var _ net.Addr = simpleAddr{} diff --git a/infrastructure/network/addressmanager/network.go b/infrastructure/network/addressmanager/network.go index 262409354..acdd94fee 100644 --- a/infrastructure/network/addressmanager/network.go +++ b/infrastructure/network/addressmanager/network.go @@ -5,8 +5,9 @@ package addressmanager import ( - "github.com/kaspanet/kaspad/app/appmessage" "net" + + "github.com/kaspanet/kaspad/app/appmessage" ) var ( @@ -77,6 +78,13 @@ var ( heNet = ipNet("2001:470::", 32, 128) ) +const ( + // GetAddressesMax is the most addresses that we will send in response + // to a getAddress (in practise the most addresses we will return from a + // call to AddressCache()). + GetAddressesMax = 2500 +) + // ipNet returns a net.IPNet struct given the passed IP address string, number // of one bits to include at the start of the mask, and the total number of bits // for the mask. @@ -199,8 +207,8 @@ func IsValid(na *appmessage.NetAddress) bool { // IsRoutable returns whether or not the passed address is routable over // the public internet. This is true as long as the address is valid and is not // in any reserved ranges. -func (am *AddressManager) IsRoutable(na *appmessage.NetAddress) bool { - if am.cfg.NetParams().AcceptUnroutable { +func IsRoutable(na *appmessage.NetAddress, acceptUnroutable bool) bool { + if acceptUnroutable { return !IsLocal(na) } @@ -218,7 +226,7 @@ func (am *AddressManager) GroupKey(na *appmessage.NetAddress) string { if IsLocal(na) { return "local" } - if !am.IsRoutable(na) { + if !IsRoutable(na, am.cfg.AcceptUnroutable) { return "unroutable" } if IsIPv4(na) { diff --git a/infrastructure/network/addressmanager/network_test.go b/infrastructure/network/addressmanager/network_test.go index e66c7750a..6d47062ff 100644 --- a/infrastructure/network/addressmanager/network_test.go +++ b/infrastructure/network/addressmanager/network_test.go @@ -5,15 +5,16 @@ package addressmanager import ( - "github.com/kaspanet/kaspad/app/appmessage" "net" "testing" + + "github.com/kaspanet/kaspad/app/appmessage" ) // TestIPTypes ensures the various functions which determine the type of an IP // address based on RFCs work as intended. func TestIPTypes(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil) + amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP") defer teardown() type ipTest struct { in appmessage.NetAddress @@ -136,7 +137,7 @@ func TestIPTypes(t *testing.T) { t.Errorf("IsValid %s\n got: %v want: %v", test.in.IP, rv, test.valid) } - if rv := amgr.IsRoutable(&test.in); rv != test.routable { + if rv := IsRoutable(&test.in, amgr.cfg.AcceptUnroutable); rv != test.routable { t.Errorf("IsRoutable %s\n got: %v want: %v", test.in.IP, rv, test.routable) } } @@ -145,7 +146,7 @@ func TestIPTypes(t *testing.T) { // TestGroupKey tests the GroupKey function to ensure it properly groups various // IP addresses. func TestGroupKey(t *testing.T) { - amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil) + amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP") defer teardown() tests := []struct { diff --git a/infrastructure/network/addressmanager/test_utils.go b/infrastructure/network/addressmanager/test_utils.go index dc67b4050..1425ecc9c 100644 --- a/infrastructure/network/addressmanager/test_utils.go +++ b/infrastructure/network/addressmanager/test_utils.go @@ -1,11 +1,12 @@ package addressmanager import ( + "net" + "strconv" + "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/util/subnetworkid" "github.com/pkg/errors" - "net" - "strconv" ) // AddAddressByIP adds an address where we are given an ip:port and not a @@ -26,6 +27,6 @@ func AddAddressByIP(am *AddressManager, addressIP string, subnetworkID *subnetwo return errors.Errorf("invalid port %s: %s", portString, err) } netAddress := appmessage.NewNetAddressIPPort(ip, uint16(port), 0) - am.AddAddress(netAddress, netAddress, subnetworkID) + am.AddAddresses(netAddress) return nil } diff --git a/infrastructure/network/connmanager/outgoing_connections.go b/infrastructure/network/connmanager/outgoing_connections.go index e9a3bf0fd..5b46e287a 100644 --- a/infrastructure/network/connmanager/outgoing_connections.go +++ b/infrastructure/network/connmanager/outgoing_connections.go @@ -1,5 +1,7 @@ package connmanager +import "github.com/kaspanet/kaspad/app/appmessage" + // checkOutgoingConnections goes over all activeOutgoing and makes sure they are still active. // Then it opens connections so that we have targetOutgoing active connections func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) { @@ -14,6 +16,12 @@ func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) { delete(c.activeOutgoing, address) } + connections := c.netAdapter.P2PConnections() + connectedAddresses := make([]*appmessage.NetAddress, len(connections)) + for i, connection := range connections { + connectedAddresses[i] = connection.NetAddress() + } + liveConnections := len(c.activeOutgoing) if c.targetOutgoing == liveConnections { return @@ -23,47 +31,21 @@ func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) { liveConnections, c.targetOutgoing, c.targetOutgoing-liveConnections) connectionsNeededCount := c.targetOutgoing - len(c.activeOutgoing) - connectionAttempts := connectionsNeededCount * 2 - for i := 0; i < connectionAttempts; i++ { - // Return in case we've already reached or surpassed our target - if len(c.activeOutgoing) >= c.targetOutgoing { - return - } + netAddresses := c.addressManager.RandomAddresses(connectionsNeededCount, connectedAddresses) - address := c.addressManager.GetAddress() - if address == nil { - log.Warnf("No more addresses available") - return - } + for _, netAddress := range netAddresses { + addressString := netAddress.TCPAddress().String() - netAddress := address.NetAddress() - tcpAddress := netAddress.TCPAddress() - addressString := tcpAddress.String() - - if c.connectionExists(addressString) { - log.Debugf("Fetched address %s from address manager but it's already connected. Skipping...", addressString) - continue - } - - isBanned, err := c.addressManager.IsBanned(netAddress) - if err != nil { - log.Infof("Couldn't resolve whether %s is banned: %s", addressString, err) - continue - } - if isBanned { - continue - } - - c.addressManager.Attempt(netAddress) log.Debugf("Connecting to %s because we have %d outgoing connections and the target is "+ "%d", addressString, len(c.activeOutgoing), c.targetOutgoing) - err = c.initiateConnection(addressString) + + err := c.initiateConnection(addressString) if err != nil { log.Infof("Couldn't connect to %s: %s", addressString, err) + c.addressManager.RemoveAddress(netAddress) continue } - c.addressManager.Connected(netAddress) c.activeOutgoing[addressString] = struct{}{} } } diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go index 731d77ad8..16d6cf297 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go @@ -3419,8 +3419,9 @@ type GetPeerAddressesResponseMessage struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Addresses []*GetPeerAddressesKnownAddressMessage `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` - Error *RPCError `protobuf:"bytes,1000,opt,name=error,proto3" json:"error,omitempty"` + Addresses []*GetPeerAddressesKnownAddressMessage `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + BannedAddresses []*GetPeerAddressesKnownAddressMessage `protobuf:"bytes,2,rep,name=bannedAddresses,proto3" json:"bannedAddresses,omitempty"` + Error *RPCError `protobuf:"bytes,1000,opt,name=error,proto3" json:"error,omitempty"` } func (x *GetPeerAddressesResponseMessage) Reset() { @@ -3462,6 +3463,13 @@ func (x *GetPeerAddressesResponseMessage) GetAddresses() []*GetPeerAddressesKnow return nil } +func (x *GetPeerAddressesResponseMessage) GetBannedAddresses() []*GetPeerAddressesKnownAddressMessage { + if x != nil { + return x.BannedAddresses + } + return nil +} + func (x *GetPeerAddressesResponseMessage) GetError() *RPCError { if x != nil { return x.Error @@ -7079,442 +7087,447 @@ var file_messages_proto_rawDesc = []byte{ 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x20, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xf5, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0x39, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x64, - 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x64, 0x64, 0x72, 0x22, 0x22, - 0x0a, 0x20, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, - 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0x79, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x62, 0x61, 0x6e, 0x6e, 0x65, + 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4b, 0x6e, 0x6f, + 0x77, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x0f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, - 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7b, 0x0a, - 0x0c, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x66, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, - 0x59, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, - 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x33, 0x0a, 0x1d, 0x47, 0x65, - 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, - 0x7b, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4d, 0x65, 0x6d, - 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x21, 0x0a, 0x1f, - 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x81, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, - 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0x24, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x23, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x12, - 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xff, 0x02, 0x0a, 0x1b, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, - 0x49, 0x6e, 0x66, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, - 0x67, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, - 0x48, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x69, - 0x73, 0x53, 0x79, 0x6e, 0x63, 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, - 0x73, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x69, 0x73, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x74, - 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, - 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x61, 0x64, 0x76, - 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x61, 0x64, - 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, - 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x53, 0x0a, - 0x15, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, - 0x6e, 0x74, 0x22, 0x44, 0x0a, 0x16, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x49, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x48, 0x65, 0x78, 0x22, 0x62, 0x0a, 0x20, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x05, 0x65, + 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x39, 0x0a, + 0x23, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x41, 0x64, 0x64, 0x72, 0x22, 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x79, 0x0a, 0x21, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, + 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, + 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x22, 0x0a, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4f, 0x0a, 0x21, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x9e, 0x01, 0x0a, - 0x1f, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x38, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, 0x61, 0x64, - 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, - 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x62, 0x0a, - 0x0a, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, - 0x40, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x22, 0x49, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, 0x73, 0x22, 0xfa, 0x01, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x73, - 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, - 0x28, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, - 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7b, 0x0a, 0x0c, 0x4d, 0x65, 0x6d, 0x70, 0x6f, + 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x59, 0x0a, 0x16, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0xc8, 0x01, 0x0a, 0x17, 0x47, 0x65, - 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, - 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x12, + 0x44, 0x61, 0x74, 0x61, 0x22, 0x33, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, + 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, 0x7b, 0x0a, 0x1e, 0x47, 0x65, 0x74, + 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x65, + 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x21, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, + 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x20, 0x47, 0x65, + 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x31, + 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x70, + 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, + 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x24, 0x0a, + 0x22, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x69, + 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xff, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x2a, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, + 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, + 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x4e, + 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x53, 0x79, 0x6e, + 0x63, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x75, 0x74, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x75, 0x74, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, + 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x53, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x50, 0x65, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x20, 0x0a, 0x0b, 0x69, 0x73, + 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x69, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x44, 0x0a, 0x16, + 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x49, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x22, 0x62, 0x0a, + 0x20, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0x22, 0x0a, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4f, 0x0a, 0x21, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x9e, 0x01, 0x0a, 0x1f, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x62, 0x0a, 0x0a, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x0e, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0e, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x49, 0x0a, 0x0d, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x54, 0x78, 0x49, 0x64, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x78, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, + 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x22, 0xc8, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, + 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, + 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe9, + 0x05, 0x0a, 0x10, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x75, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x75, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, + 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, + 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x12, 0x26, 0x0a, + 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, + 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x75, 0x74, 0x78, + 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x48, 0x65, 0x78, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x12, 0x59, 0x0a, 0x16, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x16, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x69, + 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x48, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, + 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xe3, 0x04, 0x0a, 0x16, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, + 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x67, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x67, 0x61, 0x73, 0x12, + 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, + 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x5e, 0x0a, 0x18, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, + 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x52, 0x18, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, + 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x61, 0x0a, 0x19, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, + 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x52, 0x19, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x20, 0x0a, 0x0b, + 0x69, 0x73, 0x49, 0x6e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x69, 0x73, 0x49, 0x6e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x69, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0x9f, 0x01, 0x0a, 0x17, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, + 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x32, 0x0a, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x52, 0x09, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x63, 0x65, 0x22, 0x2f, 0x0a, 0x09, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x73, + 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x68, 0x65, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x18, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x41, 0x0a, 0x0c, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x52, 0x0c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, + 0x66, 0x0a, 0x12, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x73, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x41, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x66, 0x0a, 0x1c, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x61, + 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, + 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x79, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, + 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x94, 0x02, + 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, + 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, + 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe9, 0x05, 0x0a, 0x10, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a, - 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x75, 0x65, 0x53, - 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x75, 0x65, - 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, - 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x48, 0x65, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, - 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x68, 0x61, 0x73, - 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x61, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, - 0x6f, 0x6f, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, - 0x26, 0x0a, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, - 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x12, - 0x59, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, - 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, - 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x69, - 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x12, - 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, - 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x68, 0x69, 0x6c, 0x64, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x30, - 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, - 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x61, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x22, 0xe3, 0x04, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x68, - 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x03, 0x67, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x48, 0x61, 0x73, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x12, 0x5e, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x0b, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, - 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x18, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x73, 0x12, 0x61, 0x0a, 0x19, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x0c, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, - 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, - 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x19, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, - 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, - 0x42, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x49, 0x6e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, - 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x49, 0x6e, 0x4d, 0x65, 0x6d, - 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x17, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x32, 0x0a, 0x09, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, - 0x67, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, - 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x2f, 0x0a, 0x09, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x73, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x18, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x41, 0x0a, 0x0c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x66, 0x0a, 0x12, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x73, 0x6d, 0x12, 0x10, 0x0a, - 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x41, 0x0a, - 0x1b, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, - 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, - 0x22, 0x66, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2a, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x79, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe1, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x22, 0x94, 0x02, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, - 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2a, - 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe1, 0x01, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x48, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x12, 0x38, - 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, - 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x1d, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, - 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0xd1, - 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1e, 0x0a, - 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x12, 0x47, 0x0a, - 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, - 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, - 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x22, 0x1d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x6a, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, - 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x1f, 0x0a, - 0x1d, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x67, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, - 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x67, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, - 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, - 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x70, 0x61, 0x73, 0x74, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x54, - 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x70, 0x61, 0x73, 0x74, 0x4d, - 0x65, 0x64, 0x69, 0x61, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x76, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, - 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x44, 0x0a, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, + 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0xd1, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x1d, 0x0a, 0x1b, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x6a, 0x0a, 0x1c, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, 0x25, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, - 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x54, - 0x0a, 0x26, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x22, 0x27, 0x0a, 0x25, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x46, 0x69, - 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x54, 0x0a, - 0x26, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, 0x23, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x76, 0x69, - 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6e, - 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x5b, 0x0a, 0x2b, 0x46, 0x69, - 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x68, 0x75, 0x74, 0x44, - 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x45, 0x0a, 0x17, 0x53, 0x68, 0x75, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x41, 0x73, - 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, - 0x73, 0x41, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x61, 0x0a, 0x19, 0x47, 0x65, - 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x74, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, + 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x70, + 0x61, 0x73, 0x74, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0e, 0x70, 0x61, 0x73, 0x74, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x13, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x13, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0x55, 0x0a, 0x25, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x46, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x54, 0x0a, 0x26, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, + 0x69, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, + 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x27, + 0x0a, 0x25, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x54, 0x0a, 0x26, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, - 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x50, 0x0a, - 0x03, 0x50, 0x32, 0x50, 0x12, 0x49, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, + 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, + 0x23, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, + 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x12, 0x76, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x61, 0x73, 0x68, 0x22, 0x5b, 0x0a, 0x2b, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, + 0x68, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x68, 0x75, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x45, 0x0a, 0x17, 0x53, + 0x68, 0x75, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x41, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x41, 0x73, 0x63, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x22, 0x61, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x50, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x12, 0x49, + 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, - 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x32, - 0x50, 0x0a, 0x03, 0x52, 0x50, 0x43, 0x12, 0x49, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, - 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, - 0x01, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x32, 0x50, 0x0a, 0x03, 0x52, 0x50, 0x43, + 0x12, 0x49, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, + 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, + 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, + 0x69, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7730,48 +7743,49 @@ var file_messages_proto_depIdxs = []int32{ 30, // 103: protowire.NotifyBlockAddedResponseMessage.error:type_name -> protowire.RPCError 10, // 104: protowire.BlockAddedNotificationMessage.block:type_name -> protowire.BlockMessage 43, // 105: protowire.GetPeerAddressesResponseMessage.addresses:type_name -> protowire.GetPeerAddressesKnownAddressMessage - 30, // 106: protowire.GetPeerAddressesResponseMessage.error:type_name -> protowire.RPCError - 30, // 107: protowire.GetSelectedTipHashResponseMessage.error:type_name -> protowire.RPCError - 66, // 108: protowire.MempoolEntry.transactionVerboseData:type_name -> protowire.TransactionVerboseData - 46, // 109: protowire.GetMempoolEntryResponseMessage.entry:type_name -> protowire.MempoolEntry - 30, // 110: protowire.GetMempoolEntryResponseMessage.error:type_name -> protowire.RPCError - 46, // 111: protowire.GetMempoolEntriesResponseMessage.entries:type_name -> protowire.MempoolEntry - 30, // 112: protowire.GetMempoolEntriesResponseMessage.error:type_name -> protowire.RPCError - 53, // 113: protowire.GetConnectedPeerInfoResponseMessage.infos:type_name -> protowire.GetConnectedPeerInfoMessage - 30, // 114: protowire.GetConnectedPeerInfoResponseMessage.error:type_name -> protowire.RPCError - 30, // 115: protowire.AddPeerResponseMessage.error:type_name -> protowire.RPCError - 30, // 116: protowire.SubmitTransactionResponseMessage.error:type_name -> protowire.RPCError - 30, // 117: protowire.NotifyChainChangedResponseMessage.error:type_name -> protowire.RPCError - 61, // 118: protowire.ChainChangedNotificationMessage.addedChainBlocks:type_name -> protowire.ChainBlock - 62, // 119: protowire.ChainBlock.acceptedBlocks:type_name -> protowire.AcceptedBlock - 65, // 120: protowire.GetBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData - 30, // 121: protowire.GetBlockResponseMessage.error:type_name -> protowire.RPCError - 66, // 122: protowire.BlockVerboseData.transactionVerboseData:type_name -> protowire.TransactionVerboseData - 67, // 123: protowire.TransactionVerboseData.transactionVerboseInputs:type_name -> protowire.TransactionVerboseInput - 69, // 124: protowire.TransactionVerboseData.transactionVerboseOutputs:type_name -> protowire.TransactionVerboseOutput - 68, // 125: protowire.TransactionVerboseInput.scriptSig:type_name -> protowire.ScriptSig - 70, // 126: protowire.TransactionVerboseOutput.scriptPubKey:type_name -> protowire.ScriptPubKeyResult - 30, // 127: protowire.GetSubnetworkResponseMessage.error:type_name -> protowire.RPCError - 61, // 128: protowire.GetChainFromBlockResponseMessage.addedChainBlocks:type_name -> protowire.ChainBlock - 65, // 129: protowire.GetChainFromBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData - 30, // 130: protowire.GetChainFromBlockResponseMessage.error:type_name -> protowire.RPCError - 65, // 131: protowire.GetBlocksResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData - 30, // 132: protowire.GetBlocksResponseMessage.error:type_name -> protowire.RPCError - 30, // 133: protowire.GetBlockCountResponseMessage.error:type_name -> protowire.RPCError - 30, // 134: protowire.GetBlockDagInfoResponseMessage.error:type_name -> protowire.RPCError - 30, // 135: protowire.ResolveFinalityConflictResponseMessage.error:type_name -> protowire.RPCError - 30, // 136: protowire.NotifyFinalityConflictsResponseMessage.error:type_name -> protowire.RPCError - 30, // 137: protowire.ShutDownResponseMessage.error:type_name -> protowire.RPCError - 30, // 138: protowire.GetHeadersResponseMessage.error:type_name -> protowire.RPCError - 0, // 139: protowire.P2P.MessageStream:input_type -> protowire.KaspadMessage - 0, // 140: protowire.RPC.MessageStream:input_type -> protowire.KaspadMessage - 0, // 141: protowire.P2P.MessageStream:output_type -> protowire.KaspadMessage - 0, // 142: protowire.RPC.MessageStream:output_type -> protowire.KaspadMessage - 141, // [141:143] is the sub-list for method output_type - 139, // [139:141] is the sub-list for method input_type - 139, // [139:139] is the sub-list for extension type_name - 139, // [139:139] is the sub-list for extension extendee - 0, // [0:139] is the sub-list for field type_name + 43, // 106: protowire.GetPeerAddressesResponseMessage.bannedAddresses:type_name -> protowire.GetPeerAddressesKnownAddressMessage + 30, // 107: protowire.GetPeerAddressesResponseMessage.error:type_name -> protowire.RPCError + 30, // 108: protowire.GetSelectedTipHashResponseMessage.error:type_name -> protowire.RPCError + 66, // 109: protowire.MempoolEntry.transactionVerboseData:type_name -> protowire.TransactionVerboseData + 46, // 110: protowire.GetMempoolEntryResponseMessage.entry:type_name -> protowire.MempoolEntry + 30, // 111: protowire.GetMempoolEntryResponseMessage.error:type_name -> protowire.RPCError + 46, // 112: protowire.GetMempoolEntriesResponseMessage.entries:type_name -> protowire.MempoolEntry + 30, // 113: protowire.GetMempoolEntriesResponseMessage.error:type_name -> protowire.RPCError + 53, // 114: protowire.GetConnectedPeerInfoResponseMessage.infos:type_name -> protowire.GetConnectedPeerInfoMessage + 30, // 115: protowire.GetConnectedPeerInfoResponseMessage.error:type_name -> protowire.RPCError + 30, // 116: protowire.AddPeerResponseMessage.error:type_name -> protowire.RPCError + 30, // 117: protowire.SubmitTransactionResponseMessage.error:type_name -> protowire.RPCError + 30, // 118: protowire.NotifyChainChangedResponseMessage.error:type_name -> protowire.RPCError + 61, // 119: protowire.ChainChangedNotificationMessage.addedChainBlocks:type_name -> protowire.ChainBlock + 62, // 120: protowire.ChainBlock.acceptedBlocks:type_name -> protowire.AcceptedBlock + 65, // 121: protowire.GetBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 122: protowire.GetBlockResponseMessage.error:type_name -> protowire.RPCError + 66, // 123: protowire.BlockVerboseData.transactionVerboseData:type_name -> protowire.TransactionVerboseData + 67, // 124: protowire.TransactionVerboseData.transactionVerboseInputs:type_name -> protowire.TransactionVerboseInput + 69, // 125: protowire.TransactionVerboseData.transactionVerboseOutputs:type_name -> protowire.TransactionVerboseOutput + 68, // 126: protowire.TransactionVerboseInput.scriptSig:type_name -> protowire.ScriptSig + 70, // 127: protowire.TransactionVerboseOutput.scriptPubKey:type_name -> protowire.ScriptPubKeyResult + 30, // 128: protowire.GetSubnetworkResponseMessage.error:type_name -> protowire.RPCError + 61, // 129: protowire.GetChainFromBlockResponseMessage.addedChainBlocks:type_name -> protowire.ChainBlock + 65, // 130: protowire.GetChainFromBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 131: protowire.GetChainFromBlockResponseMessage.error:type_name -> protowire.RPCError + 65, // 132: protowire.GetBlocksResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 133: protowire.GetBlocksResponseMessage.error:type_name -> protowire.RPCError + 30, // 134: protowire.GetBlockCountResponseMessage.error:type_name -> protowire.RPCError + 30, // 135: protowire.GetBlockDagInfoResponseMessage.error:type_name -> protowire.RPCError + 30, // 136: protowire.ResolveFinalityConflictResponseMessage.error:type_name -> protowire.RPCError + 30, // 137: protowire.NotifyFinalityConflictsResponseMessage.error:type_name -> protowire.RPCError + 30, // 138: protowire.ShutDownResponseMessage.error:type_name -> protowire.RPCError + 30, // 139: protowire.GetHeadersResponseMessage.error:type_name -> protowire.RPCError + 0, // 140: protowire.P2P.MessageStream:input_type -> protowire.KaspadMessage + 0, // 141: protowire.RPC.MessageStream:input_type -> protowire.KaspadMessage + 0, // 142: protowire.P2P.MessageStream:output_type -> protowire.KaspadMessage + 0, // 143: protowire.RPC.MessageStream:output_type -> protowire.KaspadMessage + 142, // [142:144] is the sub-list for method output_type + 140, // [140:142] is the sub-list for method input_type + 140, // [140:140] is the sub-list for extension type_name + 140, // [140:140] is the sub-list for extension extendee + 0, // [0:140] is the sub-list for field type_name } func init() { file_messages_proto_init() } diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto index cbd86fd29..f9a4454eb 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto @@ -352,6 +352,7 @@ message GetPeerAddressesRequestMessage{ message GetPeerAddressesResponseMessage{ repeated GetPeerAddressesKnownAddressMessage addresses = 1; + repeated GetPeerAddressesKnownAddressMessage bannedAddresses = 2; RPCError error = 1000; } diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go index cf3e7b3cc..f000ada1c 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go @@ -19,9 +19,14 @@ func (x *KaspadMessage_GetPeerAddressesResponse) toAppMessage() (appmessage.Mess for i, address := range x.GetPeerAddressesResponse.Addresses { addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: address.Addr} } + bannedAddresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(x.GetPeerAddressesResponse.BannedAddresses)) + for i, address := range x.GetPeerAddressesResponse.BannedAddresses { + bannedAddresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: address.Addr} + } return &appmessage.GetPeerAddressesResponseMessage{ - Addresses: addresses, - Error: err, + Addresses: addresses, + BannedAddresses: bannedAddresses, + Error: err, }, nil } @@ -34,9 +39,14 @@ func (x *KaspadMessage_GetPeerAddressesResponse) fromAppMessage(message *appmess for i, address := range message.Addresses { addresses[i] = &GetPeerAddressesKnownAddressMessage{Addr: address.Addr} } + bannedAddresses := make([]*GetPeerAddressesKnownAddressMessage, len(message.BannedAddresses)) + for i, address := range message.BannedAddresses { + bannedAddresses[i] = &GetPeerAddressesKnownAddressMessage{Addr: address.Addr} + } x.GetPeerAddressesResponse = &GetPeerAddressesResponseMessage{ - Addresses: addresses, - Error: err, + Addresses: addresses, + BannedAddresses: bannedAddresses, + Error: err, } return nil } diff --git a/testing/integration/address_exchange_test.go b/testing/integration/address_exchange_test.go index fc59b268f..65eaed5a1 100644 --- a/testing/integration/address_exchange_test.go +++ b/testing/integration/address_exchange_test.go @@ -1,8 +1,9 @@ package integration import ( - "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" "testing" + + "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" ) func TestAddressExchange(t *testing.T) {